@blinklabs/dingo 0.7.0 → 0.8.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.
Files changed (187) hide show
  1. package/.dingo/blob/000001.sst +0 -0
  2. package/.dingo/blob/000001.vlog +0 -0
  3. package/.dingo/blob/DISCARD +0 -0
  4. package/.dingo/blob/KEYREGISTRY +2 -0
  5. package/.dingo/blob/MANIFEST +0 -0
  6. package/.dingo/metadata.sqlite +0 -0
  7. package/.github/workflows/golangci-lint.yml +1 -1
  8. package/.github/workflows/publish.yml +1 -7
  9. package/README.md +2 -1
  10. package/{bin/dingo → dingo} +0 -0
  11. package/dingo.yaml.example +53 -0
  12. package/package.json +2 -2
  13. package/Dockerfile +0 -25
  14. package/Makefile +0 -53
  15. package/blockfetch.go +0 -144
  16. package/chain/chain.go +0 -504
  17. package/chain/chain_test.go +0 -468
  18. package/chain/errors.go +0 -80
  19. package/chain/event.go +0 -33
  20. package/chain/iter.go +0 -64
  21. package/chainsync/chainsync.go +0 -97
  22. package/chainsync.go +0 -223
  23. package/cmd/dingo/load.go +0 -52
  24. package/cmd/dingo/main.go +0 -118
  25. package/cmd/dingo/serve.go +0 -49
  26. package/config/cardano/node.go +0 -192
  27. package/config/cardano/node_test.go +0 -85
  28. package/config/cardano/preview/README.md +0 -4
  29. package/config/cardano/preview/alonzo-genesis.json +0 -196
  30. package/config/cardano/preview/byron-genesis.json +0 -117
  31. package/config/cardano/preview/config.json +0 -114
  32. package/config/cardano/preview/conway-genesis.json +0 -297
  33. package/config/cardano/preview/shelley-genesis.json +0 -68
  34. package/config.go +0 -245
  35. package/connmanager/connection_manager.go +0 -105
  36. package/connmanager/connection_manager_test.go +0 -185
  37. package/connmanager/event.go +0 -37
  38. package/connmanager/listener.go +0 -140
  39. package/connmanager/outbound.go +0 -93
  40. package/connmanager/socket.go +0 -55
  41. package/connmanager/unix.go +0 -78
  42. package/custom-p2p-topology.json +0 -24
  43. package/custom-p2p-topology.json.backup +0 -24
  44. package/custom-p2p-topology.json.mainnet +0 -37
  45. package/database/account.go +0 -180
  46. package/database/block.go +0 -362
  47. package/database/certs.go +0 -53
  48. package/database/commit_timestamp.go +0 -77
  49. package/database/database.go +0 -118
  50. package/database/database_test.go +0 -62
  51. package/database/drep.go +0 -47
  52. package/database/epoch.go +0 -121
  53. package/database/immutable/chunk.go +0 -182
  54. package/database/immutable/immutable.go +0 -350
  55. package/database/immutable/immutable_test.go +0 -59
  56. package/database/immutable/primary.go +0 -106
  57. package/database/immutable/secondary.go +0 -103
  58. package/database/immutable/testdata/08893.chunk +0 -0
  59. package/database/immutable/testdata/08893.primary +0 -0
  60. package/database/immutable/testdata/08893.secondary +0 -0
  61. package/database/immutable/testdata/08894.chunk +0 -0
  62. package/database/immutable/testdata/08894.primary +0 -0
  63. package/database/immutable/testdata/08894.secondary +0 -0
  64. package/database/immutable/testdata/README.md +0 -4
  65. package/database/plugin/blob/badger/commit_timestamp.go +0 -50
  66. package/database/plugin/blob/badger/database.go +0 -152
  67. package/database/plugin/blob/badger/logger.go +0 -63
  68. package/database/plugin/blob/badger/metrics.go +0 -98
  69. package/database/plugin/blob/blob.go +0 -19
  70. package/database/plugin/blob/store.go +0 -40
  71. package/database/plugin/log.go +0 -27
  72. package/database/plugin/metadata/metadata.go +0 -19
  73. package/database/plugin/metadata/sqlite/account.go +0 -313
  74. package/database/plugin/metadata/sqlite/certs.go +0 -58
  75. package/database/plugin/metadata/sqlite/commit_timestamp.go +0 -68
  76. package/database/plugin/metadata/sqlite/database.go +0 -218
  77. package/database/plugin/metadata/sqlite/drep.go +0 -140
  78. package/database/plugin/metadata/sqlite/epoch.go +0 -120
  79. package/database/plugin/metadata/sqlite/models/account.go +0 -118
  80. package/database/plugin/metadata/sqlite/models/auth_committee_hot.go +0 -26
  81. package/database/plugin/metadata/sqlite/models/drep.go +0 -52
  82. package/database/plugin/metadata/sqlite/models/epoch.go +0 -31
  83. package/database/plugin/metadata/sqlite/models/models.go +0 -46
  84. package/database/plugin/metadata/sqlite/models/pool.go +0 -97
  85. package/database/plugin/metadata/sqlite/models/pparam_update.go +0 -27
  86. package/database/plugin/metadata/sqlite/models/pparams.go +0 -27
  87. package/database/plugin/metadata/sqlite/models/resign_committee_cold.go +0 -27
  88. package/database/plugin/metadata/sqlite/models/stake_vote_delegation.go +0 -27
  89. package/database/plugin/metadata/sqlite/models/tip.go +0 -26
  90. package/database/plugin/metadata/sqlite/models/update_drep.go +0 -27
  91. package/database/plugin/metadata/sqlite/models/utxo.go +0 -30
  92. package/database/plugin/metadata/sqlite/models/vote_delegation.go +0 -26
  93. package/database/plugin/metadata/sqlite/models/vote_registration_delegation.go +0 -15
  94. package/database/plugin/metadata/sqlite/pool.go +0 -240
  95. package/database/plugin/metadata/sqlite/pparams.go +0 -110
  96. package/database/plugin/metadata/sqlite/tip.go +0 -83
  97. package/database/plugin/metadata/sqlite/utxo.go +0 -292
  98. package/database/plugin/metadata/store.go +0 -198
  99. package/database/plugin/option.go +0 -190
  100. package/database/plugin/plugin.go +0 -20
  101. package/database/plugin/register.go +0 -118
  102. package/database/pparams.go +0 -145
  103. package/database/tip.go +0 -45
  104. package/database/txn.go +0 -147
  105. package/database/types/types.go +0 -74
  106. package/database/types/types_test.go +0 -83
  107. package/database/utxo.go +0 -263
  108. package/dist/artifacts.json +0 -1
  109. package/dist/checksums.txt +0 -22
  110. package/dist/config.yaml +0 -253
  111. package/dist/dingo-0.5.0-SNAPSHOT-d9431e4.tar.gz +0 -0
  112. package/dist/dingo-0.5.0-SNAPSHOT-d9431e4.tar.gz.sbom.json +0 -1
  113. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_darwin_arm64.tar.gz +0 -0
  114. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_darwin_arm64.tar.gz.sbom.json +0 -1
  115. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_darwin_x86_64.tar.gz +0 -0
  116. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_darwin_x86_64.tar.gz.sbom.json +0 -1
  117. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.apk +0 -0
  118. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.apk.sbom.json +0 -1
  119. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.deb +0 -0
  120. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.deb.sbom.json +0 -1
  121. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.rpm +0 -0
  122. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.rpm.sbom.json +0 -1
  123. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.apk +0 -0
  124. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.apk.sbom.json +0 -1
  125. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.deb +0 -0
  126. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.deb.sbom.json +0 -1
  127. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.rpm +0 -0
  128. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.rpm.sbom.json +0 -1
  129. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.tar.gz +0 -0
  130. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.tar.gz.sbom.json +0 -1
  131. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_x86_64.tar.gz +0 -0
  132. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_x86_64.tar.gz.sbom.json +0 -1
  133. package/dist/dingo_darwin_amd64_v1/dingo +0 -0
  134. package/dist/dingo_darwin_arm64_v8.0/dingo +0 -0
  135. package/dist/dingo_linux_amd64_v1/dingo +0 -0
  136. package/dist/dingo_linux_arm64_v8.0/dingo +0 -0
  137. package/dist/homebrew/dingo.rb +0 -51
  138. package/dist/metadata.json +0 -1
  139. package/event/event.go +0 -141
  140. package/event/event_test.go +0 -115
  141. package/event/metrics.go +0 -44
  142. package/go.mod +0 -98
  143. package/go.sum +0 -358
  144. package/internal/config/config.go +0 -145
  145. package/internal/config/config_test.go +0 -118
  146. package/internal/node/load.go +0 -149
  147. package/internal/node/node.go +0 -176
  148. package/internal/version/version.go +0 -33
  149. package/ledger/certs.go +0 -164
  150. package/ledger/chainsync.go +0 -504
  151. package/ledger/delta.go +0 -99
  152. package/ledger/eras/allegra.go +0 -154
  153. package/ledger/eras/alonzo.go +0 -156
  154. package/ledger/eras/babbage.go +0 -154
  155. package/ledger/eras/byron.go +0 -42
  156. package/ledger/eras/conway.go +0 -166
  157. package/ledger/eras/eras.go +0 -44
  158. package/ledger/eras/mary.go +0 -154
  159. package/ledger/eras/shelley.go +0 -164
  160. package/ledger/error.go +0 -19
  161. package/ledger/event.go +0 -50
  162. package/ledger/metrics.go +0 -53
  163. package/ledger/queries.go +0 -258
  164. package/ledger/slot.go +0 -127
  165. package/ledger/slot_test.go +0 -147
  166. package/ledger/state.go +0 -821
  167. package/ledger/view.go +0 -73
  168. package/localstatequery.go +0 -50
  169. package/localtxmonitor.go +0 -44
  170. package/localtxsubmission.go +0 -52
  171. package/mempool/consumer.go +0 -98
  172. package/mempool/mempool.go +0 -322
  173. package/node.go +0 -320
  174. package/peergov/event.go +0 -27
  175. package/peergov/peer.go +0 -67
  176. package/peergov/peergov.go +0 -290
  177. package/peersharing.go +0 -70
  178. package/preview-local-topology.json +0 -23
  179. package/topology/topology.go +0 -69
  180. package/topology/topology_test.go +0 -179
  181. package/tracing.go +0 -65
  182. package/txsubmission.go +0 -233
  183. package/utxorpc/query.go +0 -311
  184. package/utxorpc/submit.go +0 -395
  185. package/utxorpc/sync.go +0 -276
  186. package/utxorpc/utxorpc.go +0 -166
  187. package/utxorpc/watch.go +0 -310
package/ledger/state.go DELETED
@@ -1,821 +0,0 @@
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
- "encoding/hex"
19
- "errors"
20
- "fmt"
21
- "io"
22
- "log/slog"
23
- "slices"
24
- "sync"
25
- "time"
26
-
27
- "github.com/blinklabs-io/dingo/chain"
28
- "github.com/blinklabs-io/dingo/config/cardano"
29
- "github.com/blinklabs-io/dingo/database"
30
- "github.com/blinklabs-io/dingo/event"
31
- "github.com/blinklabs-io/dingo/ledger/eras"
32
- ouroboros "github.com/blinklabs-io/gouroboros"
33
- "github.com/blinklabs-io/gouroboros/cbor"
34
- "github.com/blinklabs-io/gouroboros/ledger"
35
- lcommon "github.com/blinklabs-io/gouroboros/ledger/common"
36
- ochainsync "github.com/blinklabs-io/gouroboros/protocol/chainsync"
37
- ocommon "github.com/blinklabs-io/gouroboros/protocol/common"
38
- "github.com/prometheus/client_golang/prometheus"
39
- )
40
-
41
- const (
42
- cleanupConsumedUtxosInterval = 5 * time.Minute
43
- cleanupConsumedUtxosSlotWindow = 50000 // TODO: calculate this from params (#395)
44
-
45
- validateHistoricalThreshold = 14 * (24 * time.Hour) // 2 weeks
46
- )
47
-
48
- type LedgerStateConfig struct {
49
- Logger *slog.Logger
50
- DataDir string
51
- EventBus *event.EventBus
52
- CardanoNodeConfig *cardano.CardanoNodeConfig
53
- PromRegistry prometheus.Registerer
54
- ValidateHistorical bool
55
- // Callback(s)
56
- BlockfetchRequestRangeFunc BlockfetchRequestRangeFunc
57
- }
58
-
59
- // BlockfetchRequestRangeFunc describes a callback function used to start a blockfetch request for
60
- // a range of blocks
61
- type BlockfetchRequestRangeFunc func(ouroboros.ConnectionId, ocommon.Point, ocommon.Point) error
62
-
63
- type LedgerState struct {
64
- sync.RWMutex
65
- chainsyncMutex sync.Mutex
66
- config LedgerStateConfig
67
- db *database.Database
68
- timerCleanupConsumedUtxos *time.Timer
69
- currentPParams lcommon.ProtocolParameters
70
- currentEpoch database.Epoch
71
- epochCache []database.Epoch
72
- currentEra eras.EraDesc
73
- currentTip ochainsync.Tip
74
- currentTipBlockNonce []byte
75
- metrics stateMetrics
76
- chainsyncBlockEvents []BlockfetchEvent
77
- chainsyncBlockfetchBusyTime time.Time
78
- chainsyncBlockfetchDoneChan chan struct{}
79
- chainsyncBlockfetchMutex sync.Mutex
80
- chainsyncBlockfetchWaiting bool
81
- chain *chain.Chain
82
- }
83
-
84
- func NewLedgerState(cfg LedgerStateConfig) (*LedgerState, error) {
85
- ls := &LedgerState{
86
- config: cfg,
87
- }
88
- if cfg.Logger == nil {
89
- // Create logger to throw away logs
90
- // We do this so we don't have to add guards around every log operation
91
- cfg.Logger = slog.New(slog.NewJSONHandler(io.Discard, nil))
92
- }
93
- // Init metrics
94
- ls.metrics.init(ls.config.PromRegistry)
95
- // Load database
96
- needsRecovery := false
97
- db, err := database.New(cfg.Logger, cfg.DataDir)
98
- if db == nil {
99
- ls.config.Logger.Error(
100
- "failed to create database",
101
- "error",
102
- "empty database returned",
103
- "component",
104
- "ledger",
105
- )
106
- return nil, errors.New("empty database returned")
107
- }
108
- ls.db = db
109
- if err != nil {
110
- var dbErr database.CommitTimestampError
111
- if !errors.As(err, &dbErr) {
112
- return nil, fmt.Errorf("failed to open database: %w", err)
113
- }
114
- ls.config.Logger.Warn(
115
- "database initialization error, needs recovery",
116
- "error",
117
- err,
118
- "component",
119
- "ledger",
120
- )
121
- needsRecovery = true
122
- }
123
- // Load chain
124
- chain, err := chain.NewChain(
125
- ls.db,
126
- ls.config.EventBus,
127
- true, // persistent
128
- )
129
- if err != nil {
130
- return nil, fmt.Errorf("failed to load chain: %w", err)
131
- }
132
- ls.chain = chain
133
- // Run recovery if needed
134
- if needsRecovery {
135
- if err := ls.recoverCommitTimestampConflict(); err != nil {
136
- return nil, fmt.Errorf("failed to recover database: %w", err)
137
- }
138
- }
139
- // Setup event handlers
140
- ls.config.EventBus.SubscribeFunc(
141
- ChainsyncEventType,
142
- ls.handleEventChainsync,
143
- )
144
- ls.config.EventBus.SubscribeFunc(
145
- BlockfetchEventType,
146
- ls.handleEventBlockfetch,
147
- )
148
- // Schedule periodic process to purge consumed UTxOs outside of the rollback window
149
- ls.scheduleCleanupConsumedUtxos()
150
- // Load epoch info from DB
151
- if err := ls.loadEpochs(nil); err != nil {
152
- return nil, fmt.Errorf("failed to load epoch info: %w", err)
153
- }
154
- // Load current protocol parameters from DB
155
- if err := ls.loadPParams(); err != nil {
156
- return nil, fmt.Errorf("failed to load pparams: %w", err)
157
- }
158
- // Load current tip
159
- if err := ls.loadTip(); err != nil {
160
- return nil, fmt.Errorf("failed to load tip: %w", err)
161
- }
162
- // Create genesis block
163
- if err := ls.createGenesisBlock(); err != nil {
164
- return nil, fmt.Errorf("failed to create genesis block: %w", err)
165
- }
166
- // Start goroutine to process new blocks
167
- go ls.ledgerProcessBlocks()
168
- return ls, nil
169
- }
170
-
171
- func (ls *LedgerState) recoverCommitTimestampConflict() error {
172
- // Load current ledger tip
173
- tmpTip, err := ls.db.GetTip(nil)
174
- if err != nil {
175
- return fmt.Errorf("failed to get tip: %w", err)
176
- }
177
- // Check if we can lookup tip block in chain
178
- _, err = ls.chain.BlockByPoint(tmpTip.Point, nil)
179
- if err != nil {
180
- // Rollback to raw chain tip on error
181
- chainTip := ls.chain.Tip()
182
- if err = ls.rollback(chainTip.Point); err != nil {
183
- return fmt.Errorf(
184
- "failed to rollback ledger: %w",
185
- err,
186
- )
187
- }
188
- }
189
- return nil
190
- }
191
-
192
- func (ls *LedgerState) Chain() *chain.Chain {
193
- return ls.chain
194
- }
195
-
196
- func (ls *LedgerState) Close() error {
197
- return ls.db.Close()
198
- }
199
-
200
- func (ls *LedgerState) scheduleCleanupConsumedUtxos() {
201
- ls.Lock()
202
- defer ls.Unlock()
203
- if ls.timerCleanupConsumedUtxos != nil {
204
- ls.timerCleanupConsumedUtxos.Stop()
205
- }
206
- ls.timerCleanupConsumedUtxos = time.AfterFunc(
207
- cleanupConsumedUtxosInterval,
208
- func() {
209
- defer func() {
210
- // Schedule the next run
211
- ls.scheduleCleanupConsumedUtxos()
212
- }()
213
- // Get the current tip, since we're querying by slot
214
- tip := ls.Tip()
215
- // Delete UTxOs that are marked as deleted and older than our slot window
216
- ls.config.Logger.Debug(
217
- "cleaning up consumed UTxOs",
218
- "component", "ledger",
219
- )
220
- ls.Lock()
221
- err := ls.db.UtxosDeleteConsumed(
222
- tip.Point.Slot-cleanupConsumedUtxosSlotWindow,
223
- nil,
224
- )
225
- ls.Unlock()
226
- if err != nil {
227
- ls.config.Logger.Error(
228
- "failed to cleanup consumed UTxOs",
229
- "component", "ledger",
230
- "error", err,
231
- )
232
- return
233
- }
234
- },
235
- )
236
- }
237
-
238
- func (ls *LedgerState) rollback(point ocommon.Point) error {
239
- // Start a transaction
240
- txn := ls.db.Transaction(true)
241
- err := txn.Do(func(txn *database.Txn) error {
242
- // Delete rolled-back UTxOs
243
- err := ls.db.UtxosDeleteRolledback(point.Slot, txn)
244
- if err != nil {
245
- return fmt.Errorf("remove rolled-back UTxOs: %w", err)
246
- }
247
- // Restore spent UTxOs
248
- err = ls.db.UtxosUnspend(point.Slot, txn)
249
- if err != nil {
250
- return fmt.Errorf(
251
- "restore spent UTxOs after rollback: %w",
252
- err,
253
- )
254
- }
255
- // Update tip
256
- ls.currentTip = ochainsync.Tip{
257
- Point: point,
258
- }
259
- if point.Slot > 0 {
260
- rollbackBlock, err := ls.chain.BlockByPoint(point, txn)
261
- if err != nil {
262
- return fmt.Errorf("failed to get rollback block: %w", err)
263
- }
264
- ls.currentTip.BlockNumber = rollbackBlock.Number
265
- }
266
- if err = ls.db.SetTip(ls.currentTip, txn); err != nil {
267
- return fmt.Errorf("failed to set tip: %w", err)
268
- }
269
- ls.updateTipMetrics()
270
- return nil
271
- })
272
- if err != nil {
273
- return err
274
- }
275
- // Reload tip
276
- if err := ls.loadTip(); err != nil {
277
- return fmt.Errorf("failed to load tip: %w", err)
278
- }
279
- var hash string
280
- if point.Slot == 0 {
281
- hash = "<genesis>"
282
- } else {
283
- hash = hex.EncodeToString(point.Hash)
284
- }
285
- ls.config.Logger.Info(
286
- fmt.Sprintf(
287
- "chain rolled back, new tip: %s at slot %d",
288
- hash,
289
- point.Slot,
290
- ),
291
- "component",
292
- "ledger",
293
- )
294
- return nil
295
- }
296
-
297
- func (ls *LedgerState) transitionToEra(
298
- txn *database.Txn,
299
- nextEraId uint,
300
- startEpoch uint64,
301
- addedSlot uint64,
302
- ) error {
303
- nextEra := eras.Eras[nextEraId]
304
- if nextEra.HardForkFunc != nil {
305
- // Perform hard fork
306
- // This generally means upgrading pparams from previous era
307
- newPParams, err := nextEra.HardForkFunc(
308
- ls.config.CardanoNodeConfig,
309
- ls.currentPParams,
310
- )
311
- if err != nil {
312
- return fmt.Errorf("hard fork failed: %w", err)
313
- }
314
- ls.currentPParams = newPParams
315
- ls.config.Logger.Debug(
316
- "updated protocol params",
317
- "pparams",
318
- fmt.Sprintf("%#v", ls.currentPParams),
319
- )
320
- // Write pparams update to DB
321
- pparamsCbor, err := cbor.Encode(&ls.currentPParams)
322
- if err != nil {
323
- return fmt.Errorf("failed to encode pparams: %w", err)
324
- }
325
- err = ls.db.SetPParams(
326
- pparamsCbor,
327
- addedSlot,
328
- startEpoch,
329
- nextEraId,
330
- txn,
331
- )
332
- if err != nil {
333
- return fmt.Errorf("failed to set pparams: %w", err)
334
- }
335
- }
336
- ls.currentEra = nextEra
337
- return nil
338
- }
339
-
340
- // consumeUtxo marks a UTxO as "deleted" without actually deleting it. This allows for a UTxO
341
- // to be easily on rollback
342
- func (ls *LedgerState) consumeUtxo(
343
- txn *database.Txn,
344
- utxoId ledger.TransactionInput,
345
- slot uint64,
346
- ) error {
347
- return ls.db.UtxoConsume(
348
- utxoId,
349
- slot,
350
- txn,
351
- )
352
- }
353
-
354
- func (ls *LedgerState) ledgerProcessBlocks() {
355
- iter, err := ls.chain.FromPoint(ls.currentTip.Point, false)
356
- if err != nil {
357
- ls.config.Logger.Error(
358
- "failed to create chain iterator: " + err.Error(),
359
- )
360
- return
361
- }
362
- shouldBlock := false
363
- shouldValidate := ls.config.ValidateHistorical
364
- // We chose 500 as an arbitrary max batch size. A "chain extended" message will be logged after each batch
365
- nextBatch := make([]*chain.ChainIteratorResult, 0, 500)
366
- var next, nextRollback, cachedNext *chain.ChainIteratorResult
367
- var tmpBlock ledger.Block
368
- var needsRollback, needsEpochRollover bool
369
- var nextEpochEraId uint
370
- var end, i int
371
- var txn *database.Txn
372
- for {
373
- if needsEpochRollover {
374
- needsEpochRollover = false
375
- txn := ls.db.Transaction(true)
376
- err := txn.Do(func(txn *database.Txn) error {
377
- // Check for era change
378
- if nextEpochEraId != ls.currentEra.Id {
379
- // Transition through every era between the current and the target era
380
- for nextEraId := ls.currentEra.Id + 1; nextEraId <= nextEpochEraId; nextEraId++ {
381
- if err := ls.transitionToEra(txn, nextEraId, ls.currentEpoch.EpochId, ls.currentEpoch.StartSlot+uint64(ls.currentEpoch.LengthInSlots)); err != nil {
382
- return err
383
- }
384
- }
385
- }
386
- // Process epoch rollover
387
- if err := ls.processEpochRollover(txn); err != nil {
388
- return err
389
- }
390
- return nil
391
- })
392
- if err != nil {
393
- ls.config.Logger.Error(
394
- "failed to process epoch rollover: " + err.Error(),
395
- )
396
- return
397
- }
398
- }
399
- // Gather up next batch of blocks
400
- for {
401
- if cachedNext != nil {
402
- next = cachedNext
403
- cachedNext = nil
404
- } else {
405
- next, err = iter.Next(shouldBlock)
406
- shouldBlock = false
407
- if err != nil {
408
- if !errors.Is(err, chain.ErrIteratorChainTip) {
409
- ls.config.Logger.Error(
410
- "failed to get next block from chain iterator: " + err.Error(),
411
- )
412
- return
413
- }
414
- shouldBlock = true
415
- // Break out of inner loop to flush DB transaction and log
416
- break
417
- }
418
- }
419
- if next == nil {
420
- ls.config.Logger.Error("next block from chain iterator is nil")
421
- return
422
- }
423
- // End batch and cache next if we get a block from after the current epoch end, or if we need the initial epoch
424
- if next.Point.Slot >= (ls.currentEpoch.StartSlot+uint64(ls.currentEpoch.LengthInSlots)) ||
425
- ls.currentEpoch.SlotLength == 0 {
426
- cachedNext = next
427
- needsEpochRollover = true
428
- // Decode next block to get era ID
429
- tmpBlock, err = next.Block.Decode()
430
- if err != nil {
431
- ls.config.Logger.Error(
432
- "failed to decode block: " + err.Error(),
433
- )
434
- return
435
- }
436
- nextEpochEraId = uint(tmpBlock.Era().Id)
437
- break
438
- }
439
- if next.Rollback {
440
- // End existing batch and cache rollback if we have any blocks in the batch
441
- // We need special processing for rollbacks below
442
- if len(nextBatch) > 0 {
443
- cachedNext = next
444
- break
445
- }
446
- needsRollback = true
447
- }
448
- // Enable validation if we're getting near current tip
449
- if !shouldValidate && len(nextBatch) == 0 {
450
- // Determine wall time for next block slot
451
- slotTime, err := ls.SlotToTime(next.Point.Slot)
452
- if err != nil {
453
- ls.config.Logger.Error(
454
- "failed to convert slot to time: " + err.Error(),
455
- )
456
- return
457
- }
458
- // Check difference from current time
459
- timeDiff := time.Since(slotTime)
460
- if timeDiff < validateHistoricalThreshold {
461
- shouldValidate = true
462
- ls.config.Logger.Debug(
463
- "enabling validation as we approach tip",
464
- )
465
- }
466
- }
467
- // Add to batch
468
- nextBatch = append(nextBatch, next)
469
- // Don't exceed our pre-allocated capacity
470
- if len(nextBatch) == cap(nextBatch) {
471
- break
472
- }
473
- }
474
- // Process rollback
475
- if needsRollback {
476
- needsRollback = false
477
- // The rollback should be alone in the batch
478
- nextRollback = nextBatch[0]
479
- ls.Lock()
480
- if err = ls.rollback(nextRollback.Point); err != nil {
481
- ls.Unlock()
482
- ls.config.Logger.Error(
483
- "failed to process rollback: " + err.Error(),
484
- )
485
- return
486
- }
487
- ls.Unlock()
488
- // Clear out batch buffer
489
- nextBatch = slices.Delete(nextBatch, 0, len(nextBatch))
490
- continue
491
- }
492
- // Process batch in groups of 50 to stay under DB txn limits
493
- for i = 0; i < len(nextBatch); i += 50 {
494
- ls.Lock()
495
- end = min(
496
- len(nextBatch),
497
- i+50,
498
- )
499
- txn = ls.db.Transaction(true)
500
- err = txn.Do(func(txn *database.Txn) error {
501
- for _, next := range nextBatch[i:end] {
502
- // Process block
503
- tmpBlock, err = next.Block.Decode()
504
- if err != nil {
505
- return fmt.Errorf("block decode failed: %w", err)
506
- }
507
- if err = ls.ledgerProcessBlock(txn, next.Point, tmpBlock, shouldValidate); err != nil {
508
- return err
509
- }
510
- // Update tip
511
- ls.currentTip = ochainsync.Tip{
512
- Point: next.Point,
513
- BlockNumber: next.Block.Number,
514
- }
515
- // Update tip block nonce
516
- ls.currentTipBlockNonce = next.Block.Nonce
517
- }
518
- // Update tip in database
519
- if err := ls.db.SetTip(ls.currentTip, txn); err != nil {
520
- return fmt.Errorf("failed to set tip: %w", err)
521
- }
522
- ls.updateTipMetrics()
523
- return nil
524
- })
525
- if err != nil {
526
- ls.Unlock()
527
- ls.config.Logger.Error(
528
- "failed to process block: " + err.Error(),
529
- )
530
- return
531
- }
532
- ls.Unlock()
533
- }
534
- if len(nextBatch) > 0 {
535
- // Clear out batch buffer
536
- nextBatch = slices.Delete(nextBatch, 0, len(nextBatch))
537
- ls.config.Logger.Info(
538
- fmt.Sprintf(
539
- "chain extended, new tip: %x at slot %d",
540
- ls.currentTip.Point.Hash,
541
- ls.currentTip.Point.Slot,
542
- ),
543
- "component",
544
- "ledger",
545
- )
546
- }
547
- }
548
- }
549
-
550
- func (ls *LedgerState) ledgerProcessBlock(
551
- txn *database.Txn,
552
- point ocommon.Point,
553
- block ledger.Block,
554
- shouldValidate bool,
555
- ) error {
556
- // Check that we're processing things in order
557
- if len(ls.currentTip.Point.Hash) > 0 {
558
- if string(
559
- block.PrevHash().Bytes(),
560
- ) != string(
561
- ls.currentTip.Point.Hash,
562
- ) {
563
- return fmt.Errorf(
564
- "block %s (with prev hash %s) does not fit on current chain tip (%x)",
565
- block.Hash().String(),
566
- block.PrevHash().String(),
567
- ls.currentTip.Point.Hash,
568
- )
569
- }
570
- }
571
- // Process transactions
572
- var delta *LedgerDelta
573
- for _, tx := range block.Transactions() {
574
- if delta == nil {
575
- delta = &LedgerDelta{
576
- Point: point,
577
- }
578
- }
579
- // Validate transaction
580
- if shouldValidate {
581
- if ls.currentEra.ValidateTxFunc != nil {
582
- lv := &LedgerView{
583
- txn: txn,
584
- ls: ls,
585
- }
586
- err := ls.currentEra.ValidateTxFunc(
587
- tx,
588
- point.Slot,
589
- lv,
590
- ls.currentPParams,
591
- )
592
- if err != nil {
593
- ls.config.Logger.Warn(
594
- "TX " + tx.Hash().
595
- String() +
596
- " failed validation: " + err.Error(),
597
- )
598
- // return fmt.Errorf("TX validation failure: %w", err)
599
- }
600
- }
601
- }
602
- // Populate ledger delta from transaction
603
- if err := delta.processTransaction(tx); err != nil {
604
- return fmt.Errorf("process transaction: %w", err)
605
- }
606
- // Apply delta immediately if we may need the data to validate the next TX
607
- if shouldValidate {
608
- if err := delta.apply(ls, txn); err != nil {
609
- return err
610
- }
611
- delta = nil
612
- }
613
- }
614
- if delta != nil {
615
- if err := delta.apply(ls, txn); err != nil {
616
- return fmt.Errorf("apply ledger delta: %w", err)
617
- }
618
- }
619
- return nil
620
- }
621
-
622
- func (ls *LedgerState) updateTipMetrics() {
623
- // Update metrics
624
- ls.metrics.blockNum.Set(float64(ls.currentTip.BlockNumber))
625
- ls.metrics.slotNum.Set(float64(ls.currentTip.Point.Slot))
626
- ls.metrics.slotInEpoch.Set(
627
- float64(ls.currentTip.Point.Slot - ls.currentEpoch.StartSlot),
628
- )
629
- }
630
-
631
- func (ls *LedgerState) loadPParams() error {
632
- pparams, err := ls.db.GetPParams(
633
- ls.currentEpoch.EpochId,
634
- ls.currentEra.DecodePParamsFunc,
635
- nil,
636
- )
637
- if err != nil {
638
- return err
639
- }
640
- ls.currentPParams = pparams
641
- return nil
642
- }
643
-
644
- func (ls *LedgerState) loadEpochs(txn *database.Txn) error {
645
- // Load and cache all epochs
646
- epochs, err := ls.db.GetEpochs(txn)
647
- if err != nil {
648
- return err
649
- }
650
- ls.epochCache = epochs
651
- // Set current epoch
652
- if len(epochs) > 0 {
653
- ls.currentEpoch = epochs[len(epochs)-1]
654
- ls.currentEra = eras.Eras[ls.currentEpoch.EraId]
655
- }
656
- // Update metrics
657
- ls.metrics.epochNum.Set(float64(ls.currentEpoch.EpochId))
658
- return nil
659
- }
660
-
661
- func (ls *LedgerState) loadTip() error {
662
- tmpTip, err := ls.db.GetTip(nil)
663
- if err != nil {
664
- return err
665
- }
666
- ls.currentTip = tmpTip
667
- // Load tip block and set cached block nonce
668
- if ls.currentTip.Point.Slot > 0 {
669
- tipBlock, err := ls.chain.BlockByPoint(ls.currentTip.Point, nil)
670
- if err != nil {
671
- return fmt.Errorf("failed to get tip block: %w", err)
672
- }
673
- ls.currentTipBlockNonce = tipBlock.Nonce
674
- }
675
- ls.updateTipMetrics()
676
- return nil
677
- }
678
-
679
- func (ls *LedgerState) GetBlock(point ocommon.Point) (*database.Block, error) {
680
- ret, err := ls.chain.BlockByPoint(point, nil)
681
- if err != nil {
682
- return nil, err
683
- }
684
- return &ret, nil
685
- }
686
-
687
- // RecentChainPoints returns the requested count of recent chain points in descending order. This is used mostly
688
- // for building a set of intersect points when acting as a chainsync client
689
- func (ls *LedgerState) RecentChainPoints(count int) ([]ocommon.Point, error) {
690
- tmpBlocks, err := database.BlocksRecent(ls.db, count)
691
- if err != nil {
692
- return nil, err
693
- }
694
- ret := []ocommon.Point{}
695
- var tmpBlock database.Block
696
- for _, tmpBlock = range tmpBlocks {
697
- ret = append(
698
- ret,
699
- ocommon.NewPoint(tmpBlock.Slot, tmpBlock.Hash),
700
- )
701
- }
702
- return ret, nil
703
- }
704
-
705
- // GetIntersectPoint returns the intersect between the specified points and the current chain
706
- func (ls *LedgerState) GetIntersectPoint(
707
- points []ocommon.Point,
708
- ) (*ocommon.Point, error) {
709
- tip := ls.Tip()
710
- var ret ocommon.Point
711
- var tmpBlock database.Block
712
- var err error
713
- foundOrigin := false
714
- txn := ls.db.Transaction(false)
715
- err = txn.Do(func(txn *database.Txn) error {
716
- for _, point := range points {
717
- // Ignore points with a slot later than our current tip
718
- if point.Slot > tip.Point.Slot {
719
- continue
720
- }
721
- // Ignore points with a slot earlier than an existing match
722
- if point.Slot < ret.Slot {
723
- continue
724
- }
725
- // Check for special origin point
726
- if point.Slot == 0 && len(point.Hash) == 0 {
727
- foundOrigin = true
728
- continue
729
- }
730
- // Lookup block in metadata DB
731
- tmpBlock, err = ls.chain.BlockByPoint(point, txn)
732
- if err != nil {
733
- if errors.Is(err, chain.ErrBlockNotFound) {
734
- continue
735
- }
736
- return fmt.Errorf("failed to get block: %w", err)
737
- }
738
- // Update return value
739
- ret.Slot = tmpBlock.Slot
740
- ret.Hash = tmpBlock.Hash
741
- }
742
- return nil
743
- })
744
- if err != nil {
745
- return nil, err
746
- }
747
- if ret.Slot > 0 || foundOrigin {
748
- return &ret, nil
749
- }
750
- return nil, nil
751
- }
752
-
753
- // GetChainFromPoint returns a ChainIterator starting at the specified point. If inclusive is true, the iterator
754
- // will start at the requested point, otherwise it will start at the next block.
755
- func (ls *LedgerState) GetChainFromPoint(
756
- point ocommon.Point,
757
- inclusive bool,
758
- ) (*chain.ChainIterator, error) {
759
- return ls.chain.FromPoint(point, inclusive)
760
- }
761
-
762
- // Tip returns the current chain tip
763
- func (ls *LedgerState) Tip() ochainsync.Tip {
764
- return ls.currentTip
765
- }
766
-
767
- // GetCurrentPParams returns the currentPParams value
768
- func (ls *LedgerState) GetCurrentPParams() lcommon.ProtocolParameters {
769
- return ls.currentPParams
770
- }
771
-
772
- // UtxoByRef returns a single UTxO by reference
773
- func (ls *LedgerState) UtxoByRef(
774
- txId []byte,
775
- outputIdx uint32,
776
- ) (database.Utxo, error) {
777
- return ls.db.UtxoByRef(txId, outputIdx, nil)
778
- }
779
-
780
- // UtxosByAddress returns all UTxOs that belong to the specified address
781
- func (ls *LedgerState) UtxosByAddress(
782
- addr ledger.Address,
783
- ) ([]database.Utxo, error) {
784
- ret := []database.Utxo{}
785
- utxos, err := ls.db.UtxosByAddress(addr, nil)
786
- if err != nil {
787
- return ret, err
788
- }
789
- var tmpUtxo database.Utxo
790
- for _, utxo := range utxos {
791
- tmpUtxo = database.Utxo(utxo)
792
- ret = append(ret, tmpUtxo)
793
- }
794
- return ret, nil
795
- }
796
-
797
- // ValidateTx runs ledger validation on the provided transaction
798
- func (ls *LedgerState) ValidateTx(
799
- tx lcommon.Transaction,
800
- ) error {
801
- if ls.currentEra.ValidateTxFunc != nil {
802
- txn := ls.db.Transaction(false)
803
- err := txn.Do(func(txn *database.Txn) error {
804
- lv := &LedgerView{
805
- txn: txn,
806
- ls: ls,
807
- }
808
- err := ls.currentEra.ValidateTxFunc(
809
- tx,
810
- ls.currentTip.Point.Slot,
811
- lv,
812
- ls.currentPParams,
813
- )
814
- return err
815
- })
816
- if err != nil {
817
- return fmt.Errorf("TX %s failed validation: %w", tx.Hash(), err)
818
- }
819
- }
820
- return nil
821
- }