1
1
package internal
2
2
3
3
import (
4
+ "bytes"
4
5
"context"
5
6
"errors"
6
7
"fmt"
@@ -15,6 +16,8 @@ type Account struct {
15
16
Participation * api.AccountParticipation
16
17
// IncentiveEligible determines the minimum fee
17
18
IncentiveEligible bool
19
+ // NonResidentKey finds an online account that is missing locally
20
+ NonResidentKey bool
18
21
// Account Address is the algorand encoded address
19
22
Address string
20
23
// Status is the Online/Offline/"NotParticipating" status of the account
@@ -26,10 +29,10 @@ type Account struct {
26
29
// A count of how many participation Keys exist on this node for this Account
27
30
Keys int
28
31
// Expires is the date the participation key will expire
29
- Expires time.Time
32
+ Expires * time.Time
30
33
}
31
34
32
- // Get Online Status of Account
35
+ // GetAccount status of api. Account
33
36
func GetAccount (client api.ClientWithResponsesInterface , address string ) (api.Account , error ) {
34
37
var format api.AccountInformationParamsFormat = "json"
35
38
r , err := client .AccountInformationWithResponse (
@@ -51,73 +54,117 @@ func GetAccount(client api.ClientWithResponsesInterface, address string) (api.Ac
51
54
return * r .JSON200 , nil
52
55
}
53
56
54
- func GetExpiresTime (t Time , key api.ParticipationKey , state * StateModel ) time.Time {
57
+ // GetExpiresTime calculates and returns the expiration time for a participation key based on the current account state.
58
+ func GetExpiresTime (t Time , lastRound int , roundTime time.Duration , account Account ) * time.Time {
55
59
now := t .Now ()
56
- var expires = now .Add (- (time .Hour * 24 * 365 * 100 ))
57
- if key .LastBlockProposal != nil && state .Status .LastRound != 0 && state .Metrics .RoundTime != 0 {
58
- roundDiff := max (0 , * key .EffectiveLastValid - int (state .Status .LastRound ))
59
- distance := int (state .Metrics .RoundTime ) * roundDiff
60
+ var expires time.Time
61
+ if account .Status == "Online" &&
62
+ account .Participation != nil &&
63
+ lastRound != 0 &&
64
+ roundTime != 0 {
65
+ roundDiff := max (0 , account .Participation .VoteLastValid - int (lastRound ))
66
+ distance := int (roundTime ) * roundDiff
60
67
expires = now .Add (time .Duration (distance ))
68
+ return & expires
61
69
}
62
- return expires
70
+ return nil
63
71
}
64
72
65
- // AccountsFromParticipationKeys maps an array of api.ParticipationKey to a keyed map of Account
66
- func AccountsFromState (state * StateModel , t Time , client api.ClientWithResponsesInterface ) map [string ]Account {
67
- values := make (map [string ]Account )
68
- if state == nil || state .ParticipationKeys == nil {
69
- return values
70
- }
71
- for _ , key := range * state .ParticipationKeys {
72
- val , ok := values [key .Address ]
73
- if ! ok {
74
- var account = api.Account {
75
- Address : key .Address ,
76
- Status : "Unknown" ,
77
- IncentiveEligible : nil ,
78
- Amount : 0 ,
79
- }
80
- if state .Status .State != SyncingState {
81
- var err error
82
- account , err = GetAccount (client , key .Address )
83
- // TODO: handle error
84
- if err != nil {
85
- // TODO: Logging
86
- panic (err )
87
- }
88
- }
73
+ // ParticipationKeysToAccounts converts a slice of ParticipationKey objects into a map of Account objects.
74
+ // The keys parameter is a slice of pointers to ParticipationKey instances.
75
+ // The prev parameter is an optional map that allows merging of existing accounts with new ones.
76
+ // Returns a map where each key is an address from a ParticipationKey, and the value is a corresponding Account.
77
+ func ParticipationKeysToAccounts (keys * []api.ParticipationKey ) map [string ]Account {
78
+ // Allow merging of existing accounts
79
+ var accounts = make (map [string ]Account )
89
80
90
- // Check for eligibility
91
- var incentiveEligible = false
92
- if account .IncentiveEligible == nil {
93
- incentiveEligible = false
94
- } else {
95
- incentiveEligible = * account .IncentiveEligible
96
- }
81
+ // Must have keys to process
82
+ if keys == nil {
83
+ return accounts
84
+ }
97
85
98
- values [key .Address ] = Account {
99
- Participation : account .Participation ,
86
+ // Add missing Accounts
87
+ for _ , key := range * keys {
88
+ if _ , ok := accounts [key .Address ]; ! ok {
89
+ accounts [key .Address ] = Account {
90
+ Participation : nil ,
91
+ IncentiveEligible : false ,
100
92
Address : key .Address ,
101
- Status : account .Status ,
102
- Balance : account .Amount / 1000000 ,
103
- Expires : GetExpiresTime (t , key , state ),
104
- IncentiveEligible : incentiveEligible ,
93
+ Status : "Unknown" ,
94
+ Balance : 0 ,
105
95
Keys : 1 ,
96
+ Expires : nil ,
106
97
}
107
98
} else {
108
- val .Keys ++
109
- if val .Expires .Before (t .Now ()) {
110
- now := t .Now ()
111
- var expires = GetExpiresTime (t , key , state )
112
- if ! expires .Before (now ) {
113
- val .Expires = expires
114
- }
99
+ acct := accounts [key .Address ]
100
+ acct .Keys ++
101
+ accounts [key .Address ] = acct
102
+ }
103
+ }
104
+ return accounts
105
+ }
106
+
107
+ func UpdateAccountFromRPC (account Account , rpcAccount api.Account ) Account {
108
+ account .Status = rpcAccount .Status
109
+ account .Balance = rpcAccount .Amount / 1000000
110
+ account .Participation = rpcAccount .Participation
111
+
112
+ var incentiveEligible = false
113
+ if rpcAccount .IncentiveEligible == nil {
114
+ incentiveEligible = false
115
+ } else {
116
+ incentiveEligible = * rpcAccount .IncentiveEligible
117
+ }
118
+
119
+ account .IncentiveEligible = incentiveEligible
120
+
121
+ return account
122
+ }
123
+
124
+ func IsParticipationKeyActive (part api.ParticipationKey , account api.AccountParticipation ) bool {
125
+ var equal = false
126
+ if bytes .Equal (part .Key .VoteParticipationKey , account .VoteParticipationKey ) &&
127
+ part .Key .VoteLastValid == account .VoteLastValid &&
128
+ part .Key .VoteFirstValid == account .VoteFirstValid {
129
+ equal = true
130
+ }
131
+ return equal
132
+ }
133
+
134
+ func UpdateAccountExpiredTime (t Time , account Account , state * StateModel ) Account {
135
+ var nonResidentKey = true
136
+ for _ , key := range * state .ParticipationKeys {
137
+ // We have the key locally, update the residency
138
+ if account .Status == "Offline" || (key .Address == account .Address && account .Participation != nil && IsParticipationKeyActive (key , * account .Participation )) {
139
+ nonResidentKey = false
140
+ }
141
+ }
142
+ account .NonResidentKey = nonResidentKey
143
+ account .Expires = GetExpiresTime (t , int (state .Status .LastRound ), state .Metrics .RoundTime , account )
144
+ return account
145
+ }
146
+
147
+ // AccountsFromState maps an array of api.ParticipationKey to a keyed map of Account
148
+ func AccountsFromState (state * StateModel , t Time , client api.ClientWithResponsesInterface ) (map [string ]Account , error ) {
149
+ if state == nil {
150
+ return make (map [string ]Account ), nil
151
+ }
152
+
153
+ accounts := ParticipationKeysToAccounts (state .ParticipationKeys )
154
+
155
+ for _ , acct := range accounts {
156
+ // For each account, update the data from the RPC endpoint
157
+ if state .Status .State != SyncingState {
158
+ rpcAcct , err := GetAccount (client , acct .Address )
159
+ if err != nil {
160
+ return nil , err
115
161
}
116
- values [key .Address ] = val
162
+ accounts [acct .Address ] = UpdateAccountFromRPC (acct , rpcAcct )
163
+ accounts [acct .Address ] = UpdateAccountExpiredTime (t , accounts [acct .Address ], state )
117
164
}
118
165
}
119
166
120
- return values
167
+ return accounts , nil
121
168
}
122
169
123
170
func ValidateAddress (address string ) bool {
0 commit comments