• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vocdoni / vocdoni-node / 13793199355

11 Mar 2025 04:28PM UTC coverage: 62.51% (-0.03%) from 62.542%
13793199355

Pull #1405

github

p4u
vochain: allow faucet package on NewProcessTx

Signed-off-by: p4u <pau@dabax.net>
Pull Request #1405: vochain: allow faucet package on NewProcessTx

18 of 27 new or added lines in 3 files covered. (66.67%)

19 existing lines in 3 files now uncovered.

16832 of 26927 relevant lines covered (62.51%)

37950.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.89
/vochain/state/account.go
1
package state
2

3
import (
4
        "bytes"
5
        "encoding/hex"
6
        "errors"
7
        "fmt"
8

9
        "github.com/ethereum/go-ethereum/common"
10
        "go.vocdoni.io/dvote/crypto/ethereum"
11
        "go.vocdoni.io/dvote/log"
12
        "go.vocdoni.io/dvote/tree/arbo"
13
        "go.vocdoni.io/dvote/types"
14
        "go.vocdoni.io/proto/build/go/models"
15
        "google.golang.org/protobuf/proto"
16
)
17

18
// Account represents an amount of tokens, usually attached to an address.
19
// Account includes a Nonce which needs to be incremented by 1 on each transfer,
20
// an external URI link for metadata and a list of delegated addresses allowed
21
// to use the account on its behalf (in addition to himself).
22
type Account struct {
23
        models.Account
24
}
25

26
// Marshal encodes the Account and returns the serialized bytes.
27
func (a *Account) Marshal() ([]byte, error) {
×
28
        return proto.Marshal(a)
×
29
}
×
30

31
// Unmarshal decode a set of bytes.
32
func (a *Account) Unmarshal(data []byte) error {
6,611✔
33
        return proto.Unmarshal(data, a)
6,611✔
34
}
6,611✔
35

36
// Transfer moves amount from the origin Account to the dest Account.
37
func (a *Account) Transfer(dest *Account, amount uint64) error {
1,401✔
38
        if amount == 0 {
1,401✔
39
                return fmt.Errorf("cannot transfer zero amount")
×
40
        }
×
41
        if dest == nil {
1,401✔
42
                return fmt.Errorf("destination account nil")
×
43
        }
×
44
        if a.Balance < amount {
1,402✔
45
                return ErrNotEnoughBalance
1✔
46
        }
1✔
47
        if dest.Balance+amount < dest.Balance {
1,400✔
48
                return ErrBalanceOverflow
×
49
        }
×
50
        dest.Balance += amount
1,400✔
51
        a.Balance -= amount
1,400✔
52
        return nil
1,400✔
53
}
54

55
// IsDelegate checks if an address is a delegate for an account
56
func (a *Account) IsDelegate(addr common.Address) bool {
24✔
57
        for _, d := range a.DelegateAddrs {
42✔
58
                if bytes.Equal(addr.Bytes(), d) {
34✔
59
                        return true
16✔
60
                }
16✔
61
        }
62
        return false
8✔
63
}
64

65
// AddDelegate adds an address to the list of delegates for an account
66
func (a *Account) AddDelegate(addr common.Address) error {
2✔
67
        if a.IsDelegate(addr) {
2✔
68
                return fmt.Errorf("address %s is already a delegate", addr)
×
69
        }
×
70
        a.DelegateAddrs = append(a.DelegateAddrs, addr.Bytes())
2✔
71
        return nil
2✔
72
}
73

74
// DelDelegate removes an address from the list of delegates for an account
75
func (a *Account) DelDelegate(addr common.Address) error {
1✔
76
        for i, d := range a.DelegateAddrs {
2✔
77
                if !a.IsDelegate(addr) {
1✔
78
                        return fmt.Errorf("address %s is not a delegate", addr)
×
79
                }
×
80
                if bytes.Equal(addr.Bytes(), d) {
2✔
81
                        a.DelegateAddrs[i] = a.DelegateAddrs[len(a.DelegateAddrs)-1]
1✔
82
                        a.DelegateAddrs = a.DelegateAddrs[:len(a.DelegateAddrs)-1]
1✔
83
                }
1✔
84
        }
85
        return nil
1✔
86
}
87

88
// GetAccount retrieves the Account for an address.
89
// Returns a nil account and no error if the account does not exist.
90
// Committed is relative to the state on which the function is executed.
91
func (v *State) GetAccount(address common.Address, committed bool) (*Account, error) {
8,093✔
92
        var acc Account
8,093✔
93
        if !committed {
15,988✔
94
                v.tx.RLock()
7,895✔
95
                defer v.tx.RUnlock()
7,895✔
96
        }
7,895✔
97
        raw, err := v.mainTreeViewer(committed).DeepGet(address.Bytes(), StateTreeCfg(TreeAccounts))
8,093✔
98
        if errors.Is(err, arbo.ErrKeyNotFound) {
9,593✔
99
                return nil, nil
1,500✔
100
        } else if err != nil {
8,095✔
101
                return nil, err
2✔
102
        }
2✔
103
        return &acc, acc.Unmarshal(raw)
6,591✔
104
}
105

106
// AccountBalance retrieves the Account.Balance for an address.
107
// Returns 0 and no error if the account does not exist.
108
// Committed is relative to the state on which the function is executed.
109
func (v *State) AccountBalance(address common.Address, committed bool) (uint64, error) {
2,132✔
110
        acc, err := v.GetAccount(address, committed)
2,132✔
111
        if err != nil {
2,132✔
112
                return 0, err
×
113
        }
×
114
        if acc == nil {
2,808✔
115
                return 0, nil
676✔
116
        }
676✔
117
        return acc.Balance, nil
1,456✔
118
}
119

120
// CountAccounts returns the overall number of accounts the vochain has
121
func (v *State) CountAccounts(committed bool) (uint64, error) {
379✔
122
        // TODO: Once statedb.TreeView.Size() works, replace this by that.
379✔
123
        if !committed {
379✔
124
                v.tx.RLock()
×
125
                defer v.tx.RUnlock()
×
126
        }
×
127
        accountsTree, err := v.mainTreeViewer(committed).SubTree(StateTreeCfg(TreeAccounts))
379✔
128
        if err != nil {
379✔
129
                return 0, err
×
130
        }
×
131
        return accountsTree.Size()
379✔
132
}
133

134
// ListAccounts returns the full list of accounts the vochain has, as a map indexed by the account address
135
func (v *State) ListAccounts(committed bool) (map[common.Address]*Account, error) {
1✔
136
        if !committed {
1✔
137
                v.tx.RLock()
×
138
                defer v.tx.RUnlock()
×
139
        }
×
140
        accountsTree, err := v.mainTreeViewer(committed).SubTree(StateTreeCfg(TreeAccounts))
1✔
141
        if err != nil {
1✔
142
                return nil, err
×
143
        }
×
144
        accts := make(map[common.Address]*Account)
1✔
145
        if err := accountsTree.Iterate(func(key []byte, value []byte) bool {
21✔
146
                accts[common.Address(key)] = &Account{}
20✔
147
                if err := accts[common.Address(key)].Unmarshal(value); err != nil {
20✔
148
                        log.Errorf("couldn't unmarshal account %x: %v", key, err)
×
149
                }
×
150
                return false
20✔
151
        }); err != nil {
×
152
                return nil, err
×
153
        }
×
154
        return accts, nil
1✔
155
}
156

157
// AccountFromSignature extracts an address from a signed message and returns an account if exists
UNCOV
158
func (v *State) AccountFromSignature(message, signature []byte) (*common.Address, *Account, error) {
×
UNCOV
159
        pubKey, err := ethereum.PubKeyFromSignature(message, signature)
×
UNCOV
160
        if err != nil {
×
161
                return &common.Address{}, nil, fmt.Errorf("cannot extract public key from signature: %w", err)
×
162
        }
×
UNCOV
163
        address, err := ethereum.AddrFromPublicKey(pubKey)
×
UNCOV
164
        if err != nil {
×
165
                return &common.Address{}, nil, fmt.Errorf("cannot extract address from public key: %w", err)
×
166
        }
×
UNCOV
167
        acc, err := v.GetAccount(address, false)
×
UNCOV
168
        if err != nil {
×
169
                return &common.Address{}, nil, fmt.Errorf("cannot get account: %w", err)
×
170
        }
×
UNCOV
171
        if acc == nil {
×
UNCOV
172
                return &common.Address{}, nil, fmt.Errorf("%w %s", ErrAccountNotExist, address.Hex())
×
UNCOV
173
        }
×
UNCOV
174
        return &address, acc, nil
×
175
}
176

177
// SetAccountInfoURI sets a given account infoURI
178
func (v *State) SetAccountInfoURI(accountAddress common.Address, infoURI string) error {
12✔
179
        acc, err := v.GetAccount(accountAddress, false)
12✔
180
        if err != nil {
12✔
181
                return err
×
182
        }
×
183
        if acc == nil {
12✔
184
                return ErrAccountNotExist
×
185
        }
×
186
        if acc.InfoURI == infoURI {
12✔
187
                return fmt.Errorf("same infoURI")
×
188
        }
×
189
        if infoURI == "" || len(infoURI) > types.MaxURLLength {
12✔
190
                return fmt.Errorf("invalid infoURI")
×
191
        }
×
192
        acc.InfoURI = infoURI
12✔
193
        log.Debugf("setting account %s infoURI %s", accountAddress, infoURI)
12✔
194
        return v.SetAccount(accountAddress, acc)
12✔
195
}
196

197
// IncrementAccountProcessIndex increments the process index by one and stores the value
198
func (v *State) IncrementAccountProcessIndex(accountAddress common.Address) error {
184✔
199
        acc, err := v.GetAccount(accountAddress, false)
184✔
200
        if err != nil {
184✔
201
                return err
×
202
        }
×
203
        if acc == nil {
184✔
204
                return ErrAccountNotExist
×
205
        }
×
206
        // safety check for overflow protection, we allow a maximum of 4M of processes per account
207
        if acc.ProcessIndex > 1<<22 {
184✔
208
                acc.ProcessIndex = 0
×
209
        }
×
210
        acc.ProcessIndex++
184✔
211
        log.Debugf("setting account %s process index to %d", accountAddress, acc.ProcessIndex)
184✔
212
        return v.SetAccount(accountAddress, acc)
184✔
213
}
214

215
// CreateAccount creates an account
216
func (v *State) CreateAccount(accountAddress common.Address, infoURI string, delegates [][]byte, initialBalance uint64) error {
760✔
217
        newAccount := &Account{}
760✔
218
        if infoURI != "" && len(infoURI) <= types.MaxURLLength {
1,130✔
219
                newAccount.InfoURI = infoURI
370✔
220
        }
370✔
221
        if len(delegates) > 0 {
760✔
222
                newAccount.DelegateAddrs = append(newAccount.DelegateAddrs, delegates...)
×
223
        }
×
224
        newAccount.Balance = initialBalance
760✔
225
        log.Debugf("creating account %s with infoURI %s balance %d and delegates %+v",
760✔
226
                accountAddress.String(),
760✔
227
                newAccount.InfoURI,
760✔
228
                newAccount.Balance,
760✔
229
                printPrettierDelegates(newAccount.DelegateAddrs),
760✔
230
        )
760✔
231
        return v.SetAccount(accountAddress, newAccount)
760✔
232
}
233

234
// SetAccount sets the given account data to the state
235
func (v *State) SetAccount(accountAddress common.Address, account *Account) error {
3,947✔
236
        accBytes, err := proto.Marshal(account)
3,947✔
237
        if err != nil {
3,947✔
238
                return err
×
239
        }
×
240
        log.Debugf("setAccount: address %s, nonce %d, infoURI %s, balance: %d, delegates: %+v, processIndex: %d",
3,947✔
241
                accountAddress.String(),
3,947✔
242
                account.Nonce,
3,947✔
243
                account.InfoURI,
3,947✔
244
                account.Balance,
3,947✔
245
                printPrettierDelegates(account.DelegateAddrs),
3,947✔
246
                account.ProcessIndex,
3,947✔
247
        )
3,947✔
248
        for _, l := range v.eventListeners {
9,051✔
249
                l.OnSetAccount(accountAddress.Bytes(), &Account{
5,104✔
250
                        models.Account{
5,104✔
251
                                Nonce:         account.Nonce,
5,104✔
252
                                InfoURI:       account.InfoURI,
5,104✔
253
                                Balance:       account.Balance,
5,104✔
254
                                DelegateAddrs: account.DelegateAddrs,
5,104✔
255
                                ProcessIndex:  account.ProcessIndex,
5,104✔
256
                        },
5,104✔
257
                })
5,104✔
258
        }
5,104✔
259
        v.tx.Lock()
3,947✔
260
        defer v.tx.Unlock()
3,947✔
261
        return v.tx.DeepSet(accountAddress.Bytes(), accBytes, StateTreeCfg(TreeAccounts))
3,947✔
262
}
263

264
// BurnTxCostIncrementNonce reduces the transaction cost from the account balance and increments nonce.
265
// If cost is set to 0, the cost is calculated from the tx type base cost.
266
// Reference is optional and can be used to store a reference to the transaction that caused the burn.
267
func (v *State) BurnTxCostIncrementNonce(accountAddress common.Address, txType models.TxType, cost uint64, reference string) error {
503✔
268
        // get tx cost
503✔
269
        if cost == 0 {
799✔
270
                var err error
296✔
271
                cost, err = v.TxBaseCost(txType, false)
296✔
272
                if err != nil {
296✔
273
                        return fmt.Errorf("burnTxCostIncrementNonce: %w", err)
×
274
                }
×
275
        }
276
        // get account
277
        acc, err := v.GetAccount(accountAddress, false)
503✔
278
        if err != nil {
503✔
279
                return fmt.Errorf("burnTxCostIncrementNonce: %w", err)
×
280
        }
×
281
        if acc == nil {
503✔
282
                return ErrAccountNotExist
×
283
        }
×
284
        if cost != 0 {
1,005✔
285
                // send cost to burn address
502✔
286
                burnAcc, err := v.GetAccount(BurnAddress, false)
502✔
287
                if err != nil {
502✔
288
                        return fmt.Errorf("burnTxCostIncrementNonce: %w", err)
×
289
                }
×
290
                if burnAcc == nil {
502✔
291
                        return fmt.Errorf("burnTxCostIncrementNonce: burn account does not exist")
×
292
                }
×
293
                if err := acc.Transfer(burnAcc, cost); err != nil {
502✔
294
                        return fmt.Errorf("burnTxCostIncrementNonce: %w", err)
×
295
                }
×
296
                log.Debugw("burning fee", "txType", txType.String(), "cost", cost, "account", accountAddress.String())
502✔
297
                if err := v.SetAccount(BurnAddress, burnAcc); err != nil {
502✔
298
                        return fmt.Errorf("burnTxCostIncrementNonce: %w", err)
×
299
                }
×
300
        }
301
        acc.Nonce++
503✔
302
        if err := v.SetAccount(accountAddress, acc); err != nil {
503✔
303
                return fmt.Errorf("burnTxCostIncrementNonce: %w", err)
×
304
        }
×
305

306
        // notify listeners
307
        for _, l := range v.eventListeners {
1,160✔
308
                l.OnSpendTokens(accountAddress.Bytes(), txType, cost, reference)
657✔
309
        }
657✔
310

311
        return nil
503✔
312
}
313

314
// SetAccountDelegate sets a set of delegates for a given account
315
func (v *State) SetAccountDelegate(accountAddr common.Address,
316
        delegateAddrs [][]byte,
317
        txType models.TxType,
318
) error {
3✔
319
        acc, err := v.GetAccount(accountAddr, false)
3✔
320
        if err != nil {
3✔
321
                return err
×
322
        }
×
323
        if acc == nil {
3✔
324
                return ErrAccountNotExist
×
325
        }
×
326
        switch txType {
3✔
327
        case models.TxType_ADD_DELEGATE_FOR_ACCOUNT:
2✔
328
                for _, delegate := range delegateAddrs {
4✔
329
                        log.Debugw("adding delegate", "account", accountAddr.Hex(), "delegate", hex.EncodeToString(delegate))
2✔
330
                        if err := acc.AddDelegate(common.BytesToAddress(delegate)); err != nil {
2✔
331
                                return fmt.Errorf("cannot add delegate, AddDelegate: %w", err)
×
332
                        }
×
333
                }
334
                return v.SetAccount(accountAddr, acc)
2✔
335
        case models.TxType_DEL_DELEGATE_FOR_ACCOUNT:
1✔
336
                for _, delegate := range delegateAddrs {
2✔
337
                        log.Debugw("deleting delegate", "account", accountAddr.Hex(), "delegate", hex.EncodeToString(delegate))
1✔
338
                        if err := acc.DelDelegate(common.BytesToAddress(delegate)); err != nil {
1✔
339
                                return fmt.Errorf("cannot delete delegate, DelDelegate: %w", err)
×
340
                        }
×
341
                }
342
                return v.SetAccount(accountAddr, acc)
1✔
343
        default:
×
344
                return fmt.Errorf("invalid tx type")
×
345
        }
346
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc