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

vocdoni / vocdoni-node / 11591814128

30 Oct 2024 10:59AM UTC coverage: 62.216% (+0.007%) from 62.209%
11591814128

Pull #1399

github

web-flow
build(deps): bump elliptic in /tree/arbo/testvectors/circom

Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.4 to 6.6.0.
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.4...v6.6.0)

---
updated-dependencies:
- dependency-name: elliptic
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #1399: build(deps): bump elliptic from 6.5.4 to 6.6.0 in /tree/arbo/testvectors/circom

16832 of 27054 relevant lines covered (62.22%)

38397.47 hits per line

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

62.13
/vochain/transaction/election_tx.go
1
package transaction
2

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

8
        "go.vocdoni.io/dvote/crypto/ethereum"
9
        "go.vocdoni.io/dvote/crypto/nacl"
10
        "go.vocdoni.io/dvote/crypto/zk/circuit"
11
        "go.vocdoni.io/dvote/log"
12
        "go.vocdoni.io/dvote/types"
13
        "go.vocdoni.io/dvote/vochain/processid"
14
        "go.vocdoni.io/dvote/vochain/results"
15
        vstate "go.vocdoni.io/dvote/vochain/state"
16
        "go.vocdoni.io/dvote/vochain/state/electionprice"
17
        "go.vocdoni.io/dvote/vochain/transaction/vochaintx"
18
        "go.vocdoni.io/proto/build/go/models"
19
)
20

21
// NewProcessTxCheck is an abstraction of ABCI checkTx for creating a new process
22
func (t *TransactionHandler) NewProcessTxCheck(vtx *vochaintx.Tx) (*models.Process, ethereum.Address, error) {
324✔
23
        if vtx.Tx == nil || vtx.Signature == nil || vtx.SignedBody == nil {
324✔
24
                return nil, ethereum.Address{}, ErrNilTx
×
25
        }
×
26
        tx := vtx.Tx.GetNewProcess()
324✔
27
        if tx.Process == nil {
324✔
28
                return nil, ethereum.Address{}, fmt.Errorf("new process data is empty")
×
29
        }
×
30
        // basic required fields check
31
        if tx.Process.VoteOptions == nil || tx.Process.EnvelopeType == nil || tx.Process.Mode == nil {
324✔
32
                return nil, ethereum.Address{}, fmt.Errorf("missing required fields (voteOptions, envelopeType or processMode)")
×
33
        }
×
34
        if tx.Process.VoteOptions.MaxCount == 0 {
324✔
35
                return nil, ethereum.Address{}, fmt.Errorf("missing vote maxCount parameter")
×
36
        }
×
37
        if vtx.Signature == nil || tx == nil || vtx.SignedBody == nil {
324✔
38
                return nil, ethereum.Address{}, fmt.Errorf("missing vtx.Signature or new process transaction")
×
39
        }
×
40

41
        // check for maxCount/maxValue overflows
42
        if tx.Process.VoteOptions.MaxCount > results.MaxQuestions {
324✔
43
                return nil, ethereum.Address{},
×
44
                        fmt.Errorf("maxCount overflows (%d, %d)",
×
45
                                results.MaxQuestions, tx.Process.VoteOptions.MaxCount)
×
46
        }
×
47
        if !(tx.Process.GetStatus() == models.ProcessStatus_READY || tx.Process.GetStatus() == models.ProcessStatus_PAUSED) {
328✔
48
                return nil, ethereum.Address{}, fmt.Errorf("status must be READY or PAUSED")
4✔
49
        }
4✔
50

51
        // run specific checks based on census origin
52
        switch tx.Process.CensusOrigin {
320✔
53
        case models.CensusOrigin_OFF_CHAIN_CA:
17✔
54
                if tx.Process.EnvelopeType.Anonymous {
17✔
55
                        return nil, ethereum.Address{}, fmt.Errorf("anonymous process not supported for CSP voting")
×
56
                }
×
57
        case models.CensusOrigin_FARCASTER_FRAME:
1✔
58
                if tx.Process.EnvelopeType.Anonymous {
1✔
59
                        return nil, ethereum.Address{}, fmt.Errorf("anonymous process not supported for Farcaster voting")
×
60
                }
×
61
                if tx.Process.EnvelopeType.EncryptedVotes {
1✔
62
                        return nil, ethereum.Address{}, fmt.Errorf("encrypted votes not supported for Farcaster voting")
×
63
                }
×
64
                if tx.Process.VoteOptions.MaxCount > 1 {
1✔
65
                        return nil, ethereum.Address{}, fmt.Errorf("multi-vote not supported for Farcaster voting")
×
66
                }
×
67
        }
68

69
        // get current timestamp from state
70
        currentTimestamp, err := t.state.Timestamp(false)
320✔
71
        if err != nil {
320✔
72
                return nil, ethereum.Address{}, fmt.Errorf("cannot get current timestamp: %w", err)
×
73
        }
×
74

75
        // for backwards compatibility with block count based processes, we transform the block count to duration timestamp.
76
        // TODO: remove once all processes are timestamp based
77
        if tx.Process.BlockCount > 0 {
354✔
78
                if tx.Process.Duration > 0 {
34✔
79
                        return nil, ethereum.Address{}, fmt.Errorf("cannot add process with both duration time and block count")
×
80
                }
×
81
                log.Warnw("deprecated block count based new process detected", "pid", hex.EncodeToString(tx.Process.ProcessId))
34✔
82
                tx.Process.Duration = tx.Process.BlockCount * uint32(types.DefaultBlockTime.Seconds())
34✔
83
                if tx.Process.StartBlock == 0 {
65✔
84
                        tx.Process.StartTime = 0
31✔
85
                } else {
34✔
86
                        height := t.state.CurrentHeight()
3✔
87
                        tx.Process.StartTime = currentTimestamp + (tx.Process.StartBlock-height)*uint32(types.DefaultBlockTime.Seconds())
3✔
88
                }
3✔
89
                tx.Process.BlockCount = 0
34✔
90
                tx.Process.StartBlock = 0
34✔
91
        }
92

93
        // check duration and start time are properly set
94
        // if start time is zero or one, the process will be enabled on the next block
95
        if tx.Process.StartTime == 0 {
621✔
96
                tx.Process.StartTime = currentTimestamp
301✔
97
        }
301✔
98
        if tx.Process.StartTime < currentTimestamp {
320✔
99
                return nil, ethereum.Address{}, fmt.Errorf("cannot add process with start time lower than the current timestamp")
×
100
        }
×
101
        if tx.Process.StartTime+tx.Process.Duration < currentTimestamp {
320✔
102
                return nil, ethereum.Address{}, fmt.Errorf("cannot add process with duration lower than the current timestamp")
×
103
        }
×
104

105
        // check MaxCensusSize is properly set and within the allowed range
106
        if err := t.checkMaxCensusSize(tx.Process); err != nil {
321✔
107
                return nil, ethereum.Address{}, err
1✔
108
        }
1✔
109

110
        // check signature
111
        addr, acc, err := t.state.AccountFromSignature(vtx.SignedBody, vtx.Signature)
319✔
112
        if err != nil {
319✔
113
                return nil, ethereum.Address{}, fmt.Errorf("could not get account: %w", err)
×
114
        }
×
115
        if addr == nil {
319✔
116
                return nil, ethereum.Address{}, fmt.Errorf("cannot get account from vtx.Signature, nil result")
×
117
        }
×
118

119
        // get Tx cost, since it is a new process, we should use the election price calculator
120
        cost := t.txElectionCostFromProcess(tx.Process)
319✔
121

319✔
122
        // check balance and nonce
319✔
123
        if acc.Balance < cost {
319✔
124
                return nil, ethereum.Address{}, fmt.Errorf("%w: required %d, got %d", vstate.ErrNotEnoughBalance, cost, acc.Balance)
×
125
        }
×
126

127
        // if organization ID is not set, use the sender address
128
        if tx.Process.EntityId == nil {
418✔
129
                tx.Process.EntityId = addr.Bytes()
99✔
130
        } else if !bytes.Equal(tx.Process.EntityId, addr.Bytes()) { // check if process entityID matches tx sender
324✔
131
                // check for a delegate
5✔
132
                entityAddress := ethereum.AddrFromBytes(tx.Process.EntityId)
5✔
133
                entityAccount, err := t.state.GetAccount(entityAddress, false)
5✔
134
                if err != nil {
5✔
135
                        return nil, ethereum.Address{}, fmt.Errorf(
×
136
                                "cannot get organization account for checking if the sender is a delegate: %w", err,
×
137
                        )
×
138
                }
×
139
                if entityAccount == nil {
5✔
140
                        return nil, ethereum.Address{}, fmt.Errorf("organization account %s does not exists", addr.Hex())
×
141
                }
×
142
                if !entityAccount.IsDelegate(*addr) {
6✔
143
                        return nil, ethereum.Address{}, fmt.Errorf(
1✔
144
                                "account %s unauthorized to create a new election on this organization", addr.Hex())
1✔
145
                }
1✔
146
        }
147

148
        // build the deterministic process ID
149
        pid, err := processid.BuildProcessID(tx.Process, t.state, processid.BuildNextProcessID)
318✔
150
        if err != nil {
318✔
151
                return nil, ethereum.Address{}, fmt.Errorf("cannot build processID: %w", err)
×
152
        }
×
153
        tx.Process.ProcessId = pid.Marshal()
318✔
154

318✔
155
        // TODO: Enable support for PreRegiser without Anonymous.  Figure out
318✔
156
        // all the required changes to support a process with a rolling census
318✔
157
        // that is not Anonymous.
318✔
158
        if tx.Process.EnvelopeType.Serial {
318✔
159
                return nil, ethereum.Address{}, fmt.Errorf("serial process not yet implemented")
×
160
        }
×
161

162
        if tx.Process.EnvelopeType.EncryptedVotes {
348✔
163
                // We consider the zero value as nil for security
30✔
164
                tx.Process.EncryptionPublicKeys = make([]string, types.KeyKeeperMaxKeyIndex)
30✔
165
                tx.Process.EncryptionPrivateKeys = make([]string, types.KeyKeeperMaxKeyIndex)
30✔
166
        }
30✔
167
        return tx.Process, ethereum.Address(*addr), nil
318✔
168
}
169

170
// SetProcessTxCheck is an abstraction of ABCI checkTx for canceling an existing process
171
func (t *TransactionHandler) SetProcessTxCheck(vtx *vochaintx.Tx) (ethereum.Address, error) {
340✔
172
        // check vtx.Signature available
340✔
173
        if vtx.Signature == nil || vtx.Tx == nil || vtx.SignedBody == nil {
340✔
174
                return ethereum.Address{}, ErrNilTx
×
175
        }
×
176
        tx := vtx.Tx.GetSetProcess()
340✔
177
        // get account from signature
340✔
178
        addr, acc, err := t.state.AccountFromSignature(vtx.SignedBody, vtx.Signature)
340✔
179
        if err != nil {
340✔
180
                return ethereum.Address{}, err
×
181
        }
×
182
        if addr == nil || acc == nil {
340✔
183
                return ethereum.Address{}, fmt.Errorf("cannot get account from signature")
×
184
        }
×
185
        // get process
186
        process, err := t.state.Process(tx.ProcessId, false)
340✔
187
        if err != nil {
340✔
188
                return ethereum.Address{}, fmt.Errorf("cannot get process %x: %w", tx.ProcessId, err)
×
189
        }
×
190
        // check process entityID matches tx sender
191
        if !bytes.Equal(process.EntityId, addr.Bytes()) {
347✔
192
                // check if delegate
7✔
193
                entityIDAddress := ethereum.AddrFromBytes(process.EntityId)
7✔
194
                entityIDAccount, err := t.state.GetAccount(entityIDAddress, true)
7✔
195
                if err != nil {
7✔
196
                        return ethereum.Address{}, fmt.Errorf(
×
197
                                "cannot get entityID account for checking if the sender is a delegate: %w", err,
×
198
                        )
×
199
                }
×
200
                if !entityIDAccount.IsDelegate(*addr) {
8✔
201
                        return ethereum.Address{}, fmt.Errorf(
1✔
202
                                "unauthorized to set process status, recovered addr is %s", addr.Hex(),
1✔
203
                        )
1✔
204
                } // is delegate
1✔
205
        }
206

207
        // get tx base cost
208
        cost, err := t.state.TxBaseCost(tx.Txtype, false)
339✔
209
        if err != nil {
339✔
210
                return ethereum.Address{}, fmt.Errorf("cannot get %s transaction cost: %w", tx.Txtype, err)
×
211
        }
×
212
        // check base cost
213
        if acc.Balance < cost {
339✔
214
                return ethereum.Address{}, vstate.ErrNotEnoughBalance
×
215
        }
×
216

217
        switch tx.Txtype {
339✔
218
        case models.TxType_SET_PROCESS_STATUS:
297✔
219
                if tx.GetStatus() == models.ProcessStatus_RESULTS {
300✔
220
                        // Status can only be set to RESULTS by the internal logic of the blockchain (see IST controller).
3✔
221
                        return ethereum.Address{}, fmt.Errorf("not authorized to set process status to RESULTS")
3✔
222
                }
3✔
223
                return ethereum.Address(*addr), t.state.SetProcessStatus(process.ProcessId, tx.GetStatus(), false)
294✔
224
        case models.TxType_SET_PROCESS_CENSUS:
36✔
225
                // If the census size is increased, sanity check the new size and compute the cost increase
36✔
226
                if tx.GetCensusSize() != 0 && (tx.GetCensusSize() != process.GetMaxCensusSize()) {
49✔
227
                        // if the new census size is smaller than the current census size, we return an error
13✔
228
                        if tx.GetCensusSize() < process.GetMaxCensusSize() {
14✔
229
                                return ethereum.Address{}, fmt.Errorf("new census size is smaller than the current census size")
1✔
230
                        }
1✔
231
                        // check if the maxCensusSize is within the allowed range
232
                        if err := t.checkMaxCensusSize(process); err != nil {
12✔
233
                                return ethereum.Address{}, err
×
234
                        }
×
235
                        cost = t.txCostIncreaseCensusSize(process, tx.GetCensusSize())
12✔
236
                        // get Tx cost, since it is a new census size, we should use the election price calculator
12✔
237
                        if acc.Balance < cost {
12✔
238
                                return ethereum.Address{}, fmt.Errorf("%w: required %d, got %d", vstate.ErrNotEnoughBalance, cost, acc.Balance)
×
239
                        }
×
240
                }
241
                return ethereum.Address(*addr), t.state.SetProcessCensus(
35✔
242
                        process.ProcessId,
35✔
243
                        tx.GetCensusRoot(),
35✔
244
                        tx.GetCensusURI(),
35✔
245
                        tx.GetCensusSize(),
35✔
246
                        false,
35✔
247
                )
35✔
248
        case models.TxType_SET_PROCESS_DURATION:
6✔
249
                // get Tx cost, since it modifies the process duration, we should use the election price calculator
6✔
250
                cost = t.txCostIncreaseDuration(process, tx.GetDuration())
6✔
251
                if acc.Balance < cost {
6✔
252
                        return ethereum.Address{}, fmt.Errorf("%w: required %d, got %d", vstate.ErrNotEnoughBalance, cost, acc.Balance)
×
253
                }
×
254
                return ethereum.Address(*addr), t.state.SetProcessDuration(process.ProcessId, tx.GetDuration(), false)
6✔
255

256
        default:
×
257
                return ethereum.Address{}, fmt.Errorf("unknown setProcess tx type: %s", tx.Txtype)
×
258
        }
259
}
260

261
// checkMaxCensusSize checks if the maxCensusSize is within the allowed range.
262
func (t *TransactionHandler) checkMaxCensusSize(proc *models.Process) error {
332✔
263
        txMaxCensusSize := proc.GetMaxCensusSize()
332✔
264
        if txMaxCensusSize == 0 {
332✔
265
                return fmt.Errorf("maxCensusSize is zero")
×
266
        }
×
267
        maxProcessSize, err := t.state.MaxProcessSize()
332✔
268
        if err != nil {
332✔
269
                return fmt.Errorf("cannot get maxProcessSize: %w", err)
×
270
        }
×
271
        if maxProcessSize > 0 && txMaxCensusSize > maxProcessSize {
333✔
272
                return fmt.Errorf("maxCensusSize is greater than the maximum allowed (%d)", maxProcessSize)
1✔
273
        }
1✔
274
        // check that the census size is not bigger than the circuit levels
275
        if proc.EnvelopeType.Anonymous && !circuit.Global().Config.SupportsCensusSize(txMaxCensusSize) {
331✔
276
                return fmt.Errorf("maxCensusSize for anonymous envelope "+
×
277
                        "cannot be bigger than the number of levels of the circuit (max:%d provided:%d)",
×
278
                        circuit.Global().Config.MaxCensusSize().Int64(), txMaxCensusSize)
×
279
        }
×
280
        return nil
331✔
281
}
282

283
func checkAddProcessKeys(tx *models.AdminTx, process *models.Process) error {
108✔
284
        if tx == nil {
108✔
285
                return ErrNilTx
×
286
        }
×
287
        if tx.KeyIndex == nil {
108✔
288
                return fmt.Errorf("key index is nil")
×
289
        }
×
290
        // check if at leat 1 key is provided and the keyIndex do not over/under flow
291
        if (tx.EncryptionPublicKey == nil) ||
108✔
292
                tx.GetKeyIndex() < 1 || tx.GetKeyIndex() > types.KeyKeeperMaxKeyIndex {
108✔
293
                return fmt.Errorf("no keys provided or invalid key index")
×
294
        }
×
295
        // check if provided keyIndex is not already used
296
        if len(process.EncryptionPublicKeys[tx.GetKeyIndex()]) > 0 {
108✔
297
                return fmt.Errorf("key index %d already exists", tx.KeyIndex)
×
298
        }
×
299
        return nil
108✔
300
}
301

302
func checkRevealProcessKeys(tx *models.AdminTx, process *models.Process) error {
111✔
303
        if tx == nil {
111✔
304
                return ErrNilTx
×
305
        }
×
306
        if process == nil {
111✔
307
                return fmt.Errorf("process is nil")
×
308
        }
×
309
        if tx.KeyIndex == nil {
111✔
310
                return fmt.Errorf("key index is nil")
×
311
        }
×
312
        // check if at leat 1 key is provided and the keyIndex do not over/under flow
313
        if (tx.EncryptionPrivateKey == nil) ||
111✔
314
                tx.GetKeyIndex() < 1 || tx.GetKeyIndex() > types.KeyKeeperMaxKeyIndex {
111✔
315
                return fmt.Errorf("no keys provided or invalid key index")
×
316
        }
×
317
        // check if provided keyIndex exists
318
        if len(process.EncryptionPublicKeys[tx.GetKeyIndex()]) < 1 {
111✔
319
                return fmt.Errorf("key index %d does not exist", tx.GetKeyIndex())
×
320
        }
×
321
        // check keys actually work
322
        if tx.EncryptionPrivateKey != nil {
222✔
323
                if priv, err := nacl.DecodePrivate(fmt.Sprintf("%x", tx.EncryptionPrivateKey)); err == nil {
222✔
324
                        pub := priv.Public().Bytes()
111✔
325
                        if fmt.Sprintf("%x", pub) != process.EncryptionPublicKeys[tx.GetKeyIndex()] {
111✔
326
                                log.Debugf("%x != %s", pub, process.EncryptionPublicKeys[tx.GetKeyIndex()])
×
327
                                return fmt.Errorf("the provided private key does not match "+
×
328
                                        "with the stored public key for index %d", tx.GetKeyIndex())
×
329
                        }
×
330
                } else {
×
331
                        return err
×
332
                }
×
333
        }
334
        return nil
111✔
335
}
336

337
// txElectionCostFromProcess calculates the cost of a new process based on the election price calculator.
338
func (t *TransactionHandler) txElectionCostFromProcess(process *models.Process) uint64 {
557✔
339
        return t.state.ElectionPriceCalc.Price(&electionprice.ElectionParameters{
557✔
340
                MaxCensusSize:           process.GetMaxCensusSize(),
557✔
341
                ElectionDuration:        process.BlockCount,
557✔
342
                ElectionDurationSeconds: process.Duration,
557✔
343
                EncryptedVotes:          process.GetEnvelopeType().EncryptedVotes,
557✔
344
                AnonymousVotes:          process.GetEnvelopeType().Anonymous,
557✔
345
                MaxVoteOverwrite:        process.GetVoteOptions().MaxVoteOverwrites,
557✔
346
        })
557✔
347
}
557✔
348

349
// txCostIncreaseCensusSize calculates the cost increase of a process based on the new census size.
350
func (t *TransactionHandler) txCostIncreaseCensusSize(process *models.Process, newSize uint64) uint64 {
18✔
351
        oldCost := t.txElectionCostFromProcess(process)
18✔
352
        oldSize := process.GetMaxCensusSize()
18✔
353
        process.MaxCensusSize = newSize
18✔
354
        newCost := t.txElectionCostFromProcess(process)
18✔
355
        process.MaxCensusSize = oldSize
18✔
356

18✔
357
        baseCost, err := t.state.TxBaseCost(models.TxType_SET_PROCESS_CENSUS, false)
18✔
358
        if err != nil {
18✔
359
                log.Errorw(err, "txCostIncreaseCensusSize: cannot get transaction base cost")
×
360
                return 0
×
361
        }
×
362
        if newCost < oldCost {
18✔
363
                log.Warnw("txCostIncreaseCensusSize: new cost is lower than the old cost", "oldCost", oldCost, "newCost", newCost)
×
364
                return baseCost
×
365
        }
×
366
        return baseCost + (newCost - oldCost)
18✔
367
}
368

369
// txCostIncreaseDuration calculates the cost increase of a process based on the new  duration.
370
func (t *TransactionHandler) txCostIncreaseDuration(process *models.Process, newDuration uint32) uint64 {
9✔
371
        oldCost := t.txElectionCostFromProcess(process)
9✔
372
        oldDuration := process.GetDuration()
9✔
373
        process.Duration = newDuration
9✔
374
        newCost := t.txElectionCostFromProcess(process)
9✔
375
        process.Duration = oldDuration
9✔
376

9✔
377
        baseCost, err := t.state.TxBaseCost(models.TxType_SET_PROCESS_DURATION, false)
9✔
378
        if err != nil {
9✔
379
                log.Errorw(err, "txCostIncreaseDuration: cannot get transaction base cost")
×
380
                return 0
×
381
        }
×
382
        if newCost < oldCost {
9✔
383
                return baseCost
×
384
        }
×
385
        return baseCost + (newCost - oldCost)
9✔
386
}
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