@blinklabs/dingo 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,7 +28,6 @@ import (
28
28
  "github.com/blinklabs-io/dingo/ledger/eras"
29
29
  ouroboros "github.com/blinklabs-io/gouroboros"
30
30
  "github.com/blinklabs-io/gouroboros/cbor"
31
- "github.com/blinklabs-io/gouroboros/ledger"
32
31
  lcommon "github.com/blinklabs-io/gouroboros/ledger/common"
33
32
  ocommon "github.com/blinklabs-io/gouroboros/protocol/common"
34
33
  )
@@ -98,7 +97,7 @@ func (ls *LedgerState) handleEventBlockfetch(evt event.Event) {
98
97
 
99
98
  func (ls *LedgerState) handleEventChainsyncRollback(e ChainsyncEvent) error {
100
99
  if err := ls.chain.Rollback(e.Point); err != nil {
101
- return err
100
+ return fmt.Errorf("chain rollback failed: %w", err)
102
101
  }
103
102
  return nil
104
103
  }
@@ -119,7 +118,7 @@ func (ls *LedgerState) handleEventChainsyncBlockHeader(e ChainsyncEvent) error {
119
118
  // Add header to chain
120
119
  if err := ls.chain.AddBlockHeader(e.BlockHeader); err != nil {
121
120
  if !errors.As(err, &chain.BlockNotFitChainTipError{}) {
122
- return err
121
+ return fmt.Errorf("failed adding chain block header: %w", err)
123
122
  }
124
123
  ls.config.Logger.Warn(
125
124
  fmt.Sprintf(
@@ -200,7 +199,7 @@ func (ls *LedgerState) processBlockEvents() error {
200
199
  err := txn.Do(func(txn *database.Txn) error {
201
200
  for _, evt := range ls.chainsyncBlockEvents[batchOffset : batchOffset+batchSize] {
202
201
  if err := ls.processBlockEvent(txn, evt); err != nil {
203
- return err
202
+ return fmt.Errorf("failed processing block event: %w", err)
204
203
  }
205
204
  }
206
205
  return nil
@@ -225,13 +224,13 @@ func (ls *LedgerState) createGenesisBlock() error {
225
224
  byronGenesis := ls.config.CardanoNodeConfig.ByronGenesis()
226
225
  genesisUtxos, err := byronGenesis.GenesisUtxos()
227
226
  if err != nil {
228
- return err
227
+ return fmt.Errorf("failed to generate genesis UTxOs: %w", err)
229
228
  }
230
229
  for _, utxo := range genesisUtxos {
231
230
  outAddr := utxo.Output.Address()
232
231
  outputCbor, err := cbor.Encode(utxo.Output)
233
232
  if err != nil {
234
- return err
233
+ return fmt.Errorf("encode UTxO: %w", err)
235
234
  }
236
235
  err = ls.db.NewUtxo(
237
236
  utxo.Id.Id().Bytes(),
@@ -264,7 +263,10 @@ func (ls *LedgerState) calculateEpochNonce(
264
263
  genesisHashBytes, err := hex.DecodeString(
265
264
  ls.config.CardanoNodeConfig.ShelleyGenesisHash,
266
265
  )
267
- return genesisHashBytes, err
266
+ if err != nil {
267
+ return nil, fmt.Errorf("decode genesis hash: %w", err)
268
+ }
269
+ return genesisHashBytes, nil
268
270
  }
269
271
  // Calculate stability window
270
272
  byronGenesis := ls.config.CardanoNodeConfig.ByronGenesis()
@@ -286,7 +288,7 @@ func (ls *LedgerState) calculateEpochNonce(
286
288
  stabilityWindowStartSlot,
287
289
  )
288
290
  if err != nil {
289
- return nil, err
291
+ return nil, fmt.Errorf("lookup block before slot: %w", err)
290
292
  }
291
293
  // Get last block in previous epoch
292
294
  blockLastPrevEpoch, err := database.BlockBeforeSlotTxn(
@@ -297,7 +299,7 @@ func (ls *LedgerState) calculateEpochNonce(
297
299
  if errors.Is(err, database.ErrBlockNotFound) {
298
300
  return blockBeforeStabilityWindow.Nonce, nil
299
301
  }
300
- return nil, err
302
+ return nil, fmt.Errorf("lookup block before slot: %w", err)
301
303
  }
302
304
  // Calculate nonce from inputs
303
305
  ret, err := lcommon.CalculateEpochNonce(
@@ -310,8 +312,10 @@ func (ls *LedgerState) calculateEpochNonce(
310
312
 
311
313
  func (ls *LedgerState) processEpochRollover(
312
314
  txn *database.Txn,
313
- point ocommon.Point,
314
315
  ) error {
316
+ epochStartSlot := ls.currentEpoch.StartSlot + uint64(
317
+ ls.currentEpoch.LengthInSlots,
318
+ )
315
319
  // Create initial epoch
316
320
  if ls.currentEpoch.SlotLength == 0 {
317
321
  // Create initial epoch record
@@ -319,14 +323,14 @@ func (ls *LedgerState) processEpochRollover(
319
323
  ls.config.CardanoNodeConfig,
320
324
  )
321
325
  if err != nil {
322
- return err
326
+ return fmt.Errorf("calculate epoch length: %w", err)
323
327
  }
324
328
  tmpNonce, err := ls.calculateEpochNonce(txn, 0)
325
329
  if err != nil {
326
- return err
330
+ return fmt.Errorf("calculate epoch nonce: %w", err)
327
331
  }
328
332
  err = ls.db.SetEpoch(
329
- 0, // start slot
333
+ epochStartSlot,
330
334
  0, // epoch
331
335
  tmpNonce,
332
336
  ls.currentEra.Id,
@@ -335,71 +339,64 @@ func (ls *LedgerState) processEpochRollover(
335
339
  txn,
336
340
  )
337
341
  if err != nil {
338
- return err
342
+ return fmt.Errorf("set epoch: %w", err)
339
343
  }
340
344
  // Reload epoch info
341
345
  if err := ls.loadEpochs(txn); err != nil {
342
- return err
346
+ return fmt.Errorf("load epochs: %w", err)
343
347
  }
344
348
  ls.config.Logger.Debug(
345
349
  "added initial epoch to DB",
346
350
  "epoch", fmt.Sprintf("%+v", ls.currentEpoch),
347
351
  "component", "ledger",
348
352
  )
353
+ return nil
349
354
  }
350
- // Check for epoch rollover
351
- if point.Slot > ls.currentEpoch.StartSlot+uint64(
352
- ls.currentEpoch.LengthInSlots,
353
- ) {
354
- // Apply pending pparam updates
355
- err := ls.db.ApplyPParamUpdates(
356
- point.Slot,
357
- ls.currentEpoch.EpochId,
358
- ls.currentEra.Id,
359
- &ls.currentPParams,
360
- ls.currentEra.DecodePParamsUpdateFunc,
361
- ls.currentEra.PParamsUpdateFunc,
362
- txn,
363
- )
364
- if err != nil {
365
- return err
366
- }
367
- // Create next epoch record
368
- epochSlotLength, epochLength, err := ls.currentEra.EpochLengthFunc(
369
- ls.config.CardanoNodeConfig,
370
- )
371
- if err != nil {
372
- return err
373
- }
374
- epochStartSlot := ls.currentEpoch.StartSlot + uint64(
375
- ls.currentEpoch.LengthInSlots,
376
- )
377
- tmpNonce, err := ls.calculateEpochNonce(txn, epochStartSlot)
378
- if err != nil {
379
- return err
380
- }
381
- err = ls.db.SetEpoch(
382
- epochStartSlot,
383
- ls.currentEpoch.EpochId+1,
384
- tmpNonce,
385
- ls.currentEra.Id,
386
- epochSlotLength,
387
- epochLength,
388
- txn,
389
- )
390
- if err != nil {
391
- return err
392
- }
393
- // Reload epoch info
394
- if err := ls.loadEpochs(txn); err != nil {
395
- return err
396
- }
397
- ls.config.Logger.Debug(
398
- "added next epoch to DB",
399
- "epoch", fmt.Sprintf("%+v", ls.currentEpoch),
400
- "component", "ledger",
401
- )
355
+ // Apply pending pparam updates
356
+ err := ls.db.ApplyPParamUpdates(
357
+ epochStartSlot,
358
+ ls.currentEpoch.EpochId,
359
+ ls.currentEra.Id,
360
+ &ls.currentPParams,
361
+ ls.currentEra.DecodePParamsUpdateFunc,
362
+ ls.currentEra.PParamsUpdateFunc,
363
+ txn,
364
+ )
365
+ if err != nil {
366
+ return fmt.Errorf("apply pparam updates: %w", err)
367
+ }
368
+ // Create next epoch record
369
+ epochSlotLength, epochLength, err := ls.currentEra.EpochLengthFunc(
370
+ ls.config.CardanoNodeConfig,
371
+ )
372
+ if err != nil {
373
+ return fmt.Errorf("calculate epoch length: %w", err)
374
+ }
375
+ tmpNonce, err := ls.calculateEpochNonce(txn, epochStartSlot)
376
+ if err != nil {
377
+ return fmt.Errorf("calculate epoch nonce: %w", err)
378
+ }
379
+ err = ls.db.SetEpoch(
380
+ epochStartSlot,
381
+ ls.currentEpoch.EpochId+1,
382
+ tmpNonce,
383
+ ls.currentEra.Id,
384
+ epochSlotLength,
385
+ epochLength,
386
+ txn,
387
+ )
388
+ if err != nil {
389
+ return fmt.Errorf("set epoch: %w", err)
390
+ }
391
+ // Reload epoch info
392
+ if err := ls.loadEpochs(txn); err != nil {
393
+ return fmt.Errorf("load epochs: %w", err)
402
394
  }
395
+ ls.config.Logger.Debug(
396
+ "added next epoch to DB",
397
+ "epoch", fmt.Sprintf("%+v", ls.currentEpoch),
398
+ "component", "ledger",
399
+ )
403
400
  return nil
404
401
  }
405
402
 
@@ -418,7 +415,7 @@ func (ls *LedgerState) processBlockEvent(
418
415
  e.Block,
419
416
  )
420
417
  if err != nil {
421
- return err
418
+ return fmt.Errorf("calculate etaV: %w", err)
422
419
  }
423
420
  blockNonce = tmpNonce
424
421
  }
@@ -427,7 +424,7 @@ func (ls *LedgerState) processBlockEvent(
427
424
  // Ignore and log errors about block not fitting on chain or matching first header
428
425
  if !errors.As(err, &chain.BlockNotFitChainTipError{}) &&
429
426
  !errors.As(err, &chain.BlockNotMatchHeaderError{}) {
430
- return err
427
+ return fmt.Errorf("add chain block: %w", err)
431
428
  }
432
429
  ls.config.Logger.Warn(
433
430
  fmt.Sprintf(
@@ -439,77 +436,6 @@ func (ls *LedgerState) processBlockEvent(
439
436
  return nil
440
437
  }
441
438
 
442
- func (ls *LedgerState) processTransaction(
443
- txn *database.Txn,
444
- tx ledger.Transaction,
445
- point ocommon.Point,
446
- ) error {
447
- // Validate transaction
448
- if ls.currentEra.ValidateTxFunc != nil {
449
- lv := &LedgerView{
450
- txn: txn,
451
- ls: ls,
452
- }
453
- err := ls.currentEra.ValidateTxFunc(
454
- tx,
455
- point.Slot,
456
- lv,
457
- ls.currentPParams,
458
- )
459
- if err != nil {
460
- ls.config.Logger.Warn(
461
- "TX " + tx.Hash().
462
- String() +
463
- " failed validation: " + err.Error(),
464
- )
465
- // return fmt.Errorf("TX validation failure: %w", err)
466
- }
467
- }
468
- // Process consumed UTxOs
469
- for _, consumed := range tx.Consumed() {
470
- if err := ls.consumeUtxo(txn, consumed, point.Slot); err != nil {
471
- return fmt.Errorf("remove consumed UTxO: %w", err)
472
- }
473
- }
474
- // Process produced UTxOs
475
- for _, produced := range tx.Produced() {
476
- outAddr := produced.Output.Address()
477
- err := ls.db.NewUtxo(
478
- produced.Id.Id().Bytes(),
479
- produced.Id.Index(),
480
- point.Slot,
481
- outAddr.PaymentKeyHash().Bytes(),
482
- outAddr.StakeKeyHash().Bytes(),
483
- produced.Output.Cbor(),
484
- txn,
485
- )
486
- if err != nil {
487
- return fmt.Errorf("add produced UTxO: %w", err)
488
- }
489
- }
490
- // XXX: generate event for each TX/UTxO?
491
- // Protocol parameter updates
492
- if updateEpoch, paramUpdates := tx.ProtocolParameterUpdates(); updateEpoch > 0 {
493
- for genesisHash, update := range paramUpdates {
494
- err := ls.db.SetPParamUpdate(
495
- genesisHash.Bytes(),
496
- update.Cbor(),
497
- point.Slot,
498
- updateEpoch,
499
- txn,
500
- )
501
- if err != nil {
502
- return err
503
- }
504
- }
505
- }
506
- // Certificates
507
- if err := ls.processTransactionCertificates(txn, point, tx); err != nil {
508
- return err
509
- }
510
- return nil
511
- }
512
-
513
439
  func (ls *LedgerState) blockfetchRequestRangeStart(
514
440
  connId ouroboros.ConnectionId,
515
441
  start ocommon.Point,
@@ -521,7 +447,7 @@ func (ls *LedgerState) blockfetchRequestRangeStart(
521
447
  end,
522
448
  )
523
449
  if err != nil {
524
- return err
450
+ return fmt.Errorf("request block range: %w", err)
525
451
  }
526
452
  // Create our blockfetch done signal channel
527
453
  ls.chainsyncBlockfetchDoneChan = make(chan struct{})
@@ -550,7 +476,7 @@ func (ls *LedgerState) handleEventBlockfetchBatchDone(e BlockfetchEvent) error {
550
476
  // Process pending block events
551
477
  if err := ls.processBlockEvents(); err != nil {
552
478
  ls.blockfetchRequestRangeCleanup(true)
553
- return err
479
+ return fmt.Errorf("process block events: %w", err)
554
480
  }
555
481
  // Check for pending block range request
556
482
  if !ls.chainsyncBlockfetchWaiting ||
@@ -0,0 +1,99 @@
1
+ // Copyright 2025 Blink Labs Software
2
+ //
3
+ // Licensed under the Apache License, Version 2.0 (the "License");
4
+ // you may not use this file except in compliance with the License.
5
+ // You may obtain a copy of the License at
6
+ //
7
+ // http://www.apache.org/licenses/LICENSE-2.0
8
+ //
9
+ // Unless required by applicable law or agreed to in writing, software
10
+ // distributed under the License is distributed on an "AS IS" BASIS,
11
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ // See the License for the specific language governing permissions and
13
+ // limitations under the License.
14
+
15
+ package ledger
16
+
17
+ import (
18
+ "fmt"
19
+ "slices"
20
+
21
+ "github.com/blinklabs-io/dingo/database"
22
+ lcommon "github.com/blinklabs-io/gouroboros/ledger/common"
23
+ ocommon "github.com/blinklabs-io/gouroboros/protocol/common"
24
+ )
25
+
26
+ type LedgerDelta struct {
27
+ Point ocommon.Point
28
+ Produced []lcommon.Utxo
29
+ Consumed []lcommon.TransactionInput
30
+ PParamUpdateEpoch uint64
31
+ PParamUpdates map[lcommon.Blake2b224]lcommon.ProtocolParameterUpdate
32
+ Certificates []lcommon.Certificate
33
+ }
34
+
35
+ // nolint:unparam
36
+ func (d *LedgerDelta) processTransaction(tx lcommon.Transaction) error {
37
+ // Consumed UTxOs
38
+ d.Consumed = slices.Concat(d.Consumed, tx.Consumed())
39
+ // Produced UTxOs
40
+ d.Produced = slices.Concat(d.Produced, tx.Produced())
41
+ // Protocol parameter updates
42
+ if updateEpoch, paramUpdates := tx.ProtocolParameterUpdates(); updateEpoch > 0 {
43
+ d.PParamUpdateEpoch = updateEpoch
44
+ if d.PParamUpdates == nil {
45
+ d.PParamUpdates = make(
46
+ map[lcommon.Blake2b224]lcommon.ProtocolParameterUpdate,
47
+ )
48
+ }
49
+ for genesisHash, update := range paramUpdates {
50
+ d.PParamUpdates[genesisHash] = update
51
+ }
52
+ }
53
+ // Certificates
54
+ d.Certificates = slices.Concat(d.Certificates, tx.Certificates())
55
+ return nil
56
+ }
57
+
58
+ func (d *LedgerDelta) apply(ls *LedgerState, txn *database.Txn) error {
59
+ // Process consumed UTxOs
60
+ for _, consumed := range d.Consumed {
61
+ if err := ls.consumeUtxo(txn, consumed, d.Point.Slot); err != nil {
62
+ return fmt.Errorf("remove consumed UTxO: %w", err)
63
+ }
64
+ }
65
+ // Process produced UTxOs
66
+ for _, produced := range d.Produced {
67
+ outAddr := produced.Output.Address()
68
+ err := ls.db.NewUtxo(
69
+ produced.Id.Id().Bytes(),
70
+ produced.Id.Index(),
71
+ d.Point.Slot,
72
+ outAddr.PaymentKeyHash().Bytes(),
73
+ outAddr.StakeKeyHash().Bytes(),
74
+ produced.Output.Cbor(),
75
+ txn,
76
+ )
77
+ if err != nil {
78
+ return fmt.Errorf("add produced UTxO: %w", err)
79
+ }
80
+ }
81
+ // Protocol parameter updates
82
+ for genesisHash, update := range d.PParamUpdates {
83
+ err := ls.db.SetPParamUpdate(
84
+ genesisHash.Bytes(),
85
+ update.Cbor(),
86
+ d.Point.Slot,
87
+ d.PParamUpdateEpoch,
88
+ txn,
89
+ )
90
+ if err != nil {
91
+ return fmt.Errorf("set pparam update: %w", err)
92
+ }
93
+ }
94
+ // Certificates
95
+ if err := ls.processTransactionCertificates(txn, d.Point, d.Certificates); err != nil {
96
+ return fmt.Errorf("process transaction certificates: %w", err)
97
+ }
98
+ return nil
99
+ }
@@ -134,8 +134,16 @@ func CertDepositConway(
134
134
  return uint64(tmpPparams.PoolDeposit), nil
135
135
  case *lcommon.RegistrationCertificate:
136
136
  return uint64(tmpPparams.KeyDeposit), nil
137
+ case *lcommon.RegistrationDrepCertificate:
138
+ return uint64(tmpPparams.DRepDeposit), nil
137
139
  case *lcommon.StakeRegistrationCertificate:
138
140
  return uint64(tmpPparams.KeyDeposit), nil
141
+ case *lcommon.StakeRegistrationDelegationCertificate:
142
+ return uint64(tmpPparams.KeyDeposit), nil
143
+ case *lcommon.StakeVoteRegistrationDelegationCertificate:
144
+ return uint64(tmpPparams.KeyDeposit), nil
145
+ case *lcommon.VoteRegistrationDelegationCertificate:
146
+ return uint64(tmpPparams.KeyDeposit), nil
139
147
  default:
140
148
  return 0, nil
141
149
  }
package/ledger/queries.go CHANGED
@@ -61,12 +61,10 @@ func (ls *LedgerState) querySystemStart() (any, error) {
61
61
  "unable to get shelley era genesis for system start",
62
62
  )
63
63
  }
64
- // Nanosecond is always within uint64 range
65
- // #nosec G115
66
64
  ret := olocalstatequery.SystemStartResult{
67
- Year: shelleyGenesis.SystemStart.Year(),
65
+ Year: *big.NewInt(int64(shelleyGenesis.SystemStart.Year())),
68
66
  Day: shelleyGenesis.SystemStart.YearDay(),
69
- Picoseconds: uint64(shelleyGenesis.SystemStart.Nanosecond() * 1000),
67
+ Picoseconds: *big.NewInt(int64(shelleyGenesis.SystemStart.Nanosecond() * 1000)),
70
68
  }
71
69
  return ret, nil
72
70
  }