@blinklabs/dingo 0.6.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 (195) hide show
  1. package/.dockerignore +5 -0
  2. package/.github/CODEOWNERS +5 -0
  3. package/.github/assets/dingo-ate-my-blockchain.png +0 -0
  4. package/.github/assets/dingo-illustration.png +0 -0
  5. package/.github/assets/dingo-logo-with-text-horizontal.png +0 -0
  6. package/.github/assets/dingo-logo-with-text.png +0 -0
  7. package/.github/dependabot.yml +19 -0
  8. package/.github/dingo-20241210.png +0 -0
  9. package/.github/dingo.md +56 -0
  10. package/.github/workflows/ci-docker.yml +36 -0
  11. package/.github/workflows/conventional-commits.yml +17 -0
  12. package/.github/workflows/go-test.yml +29 -0
  13. package/.github/workflows/golangci-lint.yml +23 -0
  14. package/.github/workflows/publish.yml +207 -0
  15. package/.golangci.yml +71 -0
  16. package/Dockerfile +25 -0
  17. package/LICENSE +201 -0
  18. package/Makefile +53 -0
  19. package/README.md +150 -0
  20. package/blockfetch.go +144 -0
  21. package/chain/chain.go +504 -0
  22. package/chain/chain_test.go +468 -0
  23. package/chain/errors.go +80 -0
  24. package/chain/event.go +33 -0
  25. package/chain/iter.go +64 -0
  26. package/chainsync/chainsync.go +97 -0
  27. package/chainsync.go +223 -0
  28. package/cmd/dingo/load.go +52 -0
  29. package/cmd/dingo/main.go +118 -0
  30. package/cmd/dingo/serve.go +49 -0
  31. package/config/cardano/node.go +192 -0
  32. package/config/cardano/node_test.go +85 -0
  33. package/config/cardano/preview/README.md +4 -0
  34. package/config/cardano/preview/alonzo-genesis.json +196 -0
  35. package/config/cardano/preview/byron-genesis.json +117 -0
  36. package/config/cardano/preview/config.json +114 -0
  37. package/config/cardano/preview/conway-genesis.json +297 -0
  38. package/config/cardano/preview/shelley-genesis.json +68 -0
  39. package/config.go +245 -0
  40. package/connmanager/connection_manager.go +105 -0
  41. package/connmanager/connection_manager_test.go +185 -0
  42. package/connmanager/event.go +37 -0
  43. package/connmanager/listener.go +140 -0
  44. package/connmanager/outbound.go +93 -0
  45. package/connmanager/socket.go +55 -0
  46. package/connmanager/unix.go +78 -0
  47. package/custom-p2p-topology.json +24 -0
  48. package/custom-p2p-topology.json.backup +24 -0
  49. package/custom-p2p-topology.json.mainnet +37 -0
  50. package/database/account.go +138 -0
  51. package/database/block.go +362 -0
  52. package/database/certs.go +53 -0
  53. package/database/commit_timestamp.go +77 -0
  54. package/database/database.go +118 -0
  55. package/database/database_test.go +62 -0
  56. package/database/drep.go +27 -0
  57. package/database/epoch.go +121 -0
  58. package/database/immutable/chunk.go +182 -0
  59. package/database/immutable/immutable.go +350 -0
  60. package/database/immutable/immutable_test.go +59 -0
  61. package/database/immutable/primary.go +106 -0
  62. package/database/immutable/secondary.go +103 -0
  63. package/database/immutable/testdata/08893.chunk +0 -0
  64. package/database/immutable/testdata/08893.primary +0 -0
  65. package/database/immutable/testdata/08893.secondary +0 -0
  66. package/database/immutable/testdata/08894.chunk +0 -0
  67. package/database/immutable/testdata/08894.primary +0 -0
  68. package/database/immutable/testdata/08894.secondary +0 -0
  69. package/database/immutable/testdata/README.md +4 -0
  70. package/database/plugin/blob/badger/commit_timestamp.go +50 -0
  71. package/database/plugin/blob/badger/database.go +152 -0
  72. package/database/plugin/blob/badger/logger.go +63 -0
  73. package/database/plugin/blob/badger/metrics.go +98 -0
  74. package/database/plugin/blob/blob.go +19 -0
  75. package/database/plugin/blob/store.go +40 -0
  76. package/database/plugin/log.go +27 -0
  77. package/database/plugin/metadata/metadata.go +19 -0
  78. package/database/plugin/metadata/sqlite/account.go +224 -0
  79. package/database/plugin/metadata/sqlite/certs.go +58 -0
  80. package/database/plugin/metadata/sqlite/commit_timestamp.go +68 -0
  81. package/database/plugin/metadata/sqlite/database.go +218 -0
  82. package/database/plugin/metadata/sqlite/epoch.go +120 -0
  83. package/database/plugin/metadata/sqlite/models/account.go +81 -0
  84. package/database/plugin/metadata/sqlite/models/auth_committee_hot.go +26 -0
  85. package/database/plugin/metadata/sqlite/models/deregistration_drep.go +26 -0
  86. package/database/plugin/metadata/sqlite/models/drep.go +27 -0
  87. package/database/plugin/metadata/sqlite/models/epoch.go +31 -0
  88. package/database/plugin/metadata/sqlite/models/models.go +45 -0
  89. package/database/plugin/metadata/sqlite/models/pool.go +97 -0
  90. package/database/plugin/metadata/sqlite/models/pparam_update.go +27 -0
  91. package/database/plugin/metadata/sqlite/models/pparams.go +27 -0
  92. package/database/plugin/metadata/sqlite/models/registration_drep.go +28 -0
  93. package/database/plugin/metadata/sqlite/models/resign_committee_cold.go +27 -0
  94. package/database/plugin/metadata/sqlite/models/stake_registration_delegation.go +27 -0
  95. package/database/plugin/metadata/sqlite/models/stake_vote_delegation.go +27 -0
  96. package/database/plugin/metadata/sqlite/models/stake_vote_registration_delegation.go +27 -0
  97. package/database/plugin/metadata/sqlite/models/tip.go +26 -0
  98. package/database/plugin/metadata/sqlite/models/update_drep.go +27 -0
  99. package/database/plugin/metadata/sqlite/models/utxo.go +30 -0
  100. package/database/plugin/metadata/sqlite/models/vote_delegation.go +26 -0
  101. package/database/plugin/metadata/sqlite/models/vote_registration_delegation.go +26 -0
  102. package/database/plugin/metadata/sqlite/pool.go +240 -0
  103. package/database/plugin/metadata/sqlite/pparams.go +110 -0
  104. package/database/plugin/metadata/sqlite/tip.go +83 -0
  105. package/database/plugin/metadata/sqlite/utxo.go +292 -0
  106. package/database/plugin/metadata/store.go +168 -0
  107. package/database/plugin/option.go +190 -0
  108. package/database/plugin/plugin.go +20 -0
  109. package/database/plugin/register.go +118 -0
  110. package/database/pparams.go +145 -0
  111. package/database/tip.go +45 -0
  112. package/database/txn.go +147 -0
  113. package/database/types/types.go +74 -0
  114. package/database/types/types_test.go +83 -0
  115. package/database/utxo.go +263 -0
  116. package/dist/artifacts.json +1 -0
  117. package/dist/checksums.txt +22 -0
  118. package/dist/config.yaml +253 -0
  119. package/dist/dingo-0.5.0-SNAPSHOT-d9431e4.tar.gz +0 -0
  120. package/dist/dingo-0.5.0-SNAPSHOT-d9431e4.tar.gz.sbom.json +1 -0
  121. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_darwin_arm64.tar.gz +0 -0
  122. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_darwin_arm64.tar.gz.sbom.json +1 -0
  123. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_darwin_x86_64.tar.gz +0 -0
  124. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_darwin_x86_64.tar.gz.sbom.json +1 -0
  125. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.apk +0 -0
  126. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.apk.sbom.json +1 -0
  127. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.deb +0 -0
  128. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.deb.sbom.json +1 -0
  129. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.rpm +0 -0
  130. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.rpm.sbom.json +1 -0
  131. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.apk +0 -0
  132. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.apk.sbom.json +1 -0
  133. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.deb +0 -0
  134. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.deb.sbom.json +1 -0
  135. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.rpm +0 -0
  136. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.rpm.sbom.json +1 -0
  137. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.tar.gz +0 -0
  138. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.tar.gz.sbom.json +1 -0
  139. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_x86_64.tar.gz +0 -0
  140. package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_x86_64.tar.gz.sbom.json +1 -0
  141. package/dist/dingo_darwin_amd64_v1/dingo +0 -0
  142. package/dist/dingo_darwin_arm64_v8.0/dingo +0 -0
  143. package/dist/dingo_linux_amd64_v1/dingo +0 -0
  144. package/dist/dingo_linux_arm64_v8.0/dingo +0 -0
  145. package/dist/homebrew/dingo.rb +51 -0
  146. package/dist/metadata.json +1 -0
  147. package/event/event.go +141 -0
  148. package/event/event_test.go +115 -0
  149. package/event/metrics.go +44 -0
  150. package/go.mod +98 -0
  151. package/go.sum +358 -0
  152. package/internal/config/config.go +145 -0
  153. package/internal/config/config_test.go +118 -0
  154. package/internal/node/load.go +149 -0
  155. package/internal/node/node.go +176 -0
  156. package/internal/version/version.go +33 -0
  157. package/ledger/certs.go +113 -0
  158. package/ledger/chainsync.go +578 -0
  159. package/ledger/eras/allegra.go +154 -0
  160. package/ledger/eras/alonzo.go +156 -0
  161. package/ledger/eras/babbage.go +154 -0
  162. package/ledger/eras/byron.go +42 -0
  163. package/ledger/eras/conway.go +158 -0
  164. package/ledger/eras/eras.go +44 -0
  165. package/ledger/eras/mary.go +154 -0
  166. package/ledger/eras/shelley.go +164 -0
  167. package/ledger/error.go +19 -0
  168. package/ledger/event.go +50 -0
  169. package/ledger/metrics.go +53 -0
  170. package/ledger/queries.go +260 -0
  171. package/ledger/slot.go +127 -0
  172. package/ledger/slot_test.go +147 -0
  173. package/ledger/state.go +726 -0
  174. package/ledger/view.go +73 -0
  175. package/localstatequery.go +50 -0
  176. package/localtxmonitor.go +44 -0
  177. package/localtxsubmission.go +52 -0
  178. package/mempool/consumer.go +98 -0
  179. package/mempool/mempool.go +322 -0
  180. package/node.go +320 -0
  181. package/package.json +33 -0
  182. package/peergov/event.go +27 -0
  183. package/peergov/peer.go +67 -0
  184. package/peergov/peergov.go +290 -0
  185. package/peersharing.go +70 -0
  186. package/preview-local-topology.json +23 -0
  187. package/topology/topology.go +69 -0
  188. package/topology/topology_test.go +179 -0
  189. package/tracing.go +65 -0
  190. package/txsubmission.go +233 -0
  191. package/utxorpc/query.go +311 -0
  192. package/utxorpc/submit.go +395 -0
  193. package/utxorpc/sync.go +276 -0
  194. package/utxorpc/utxorpc.go +166 -0
  195. package/utxorpc/watch.go +310 -0
@@ -0,0 +1,292 @@
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 sqlite
16
+
17
+ import (
18
+ "fmt"
19
+
20
+ "github.com/blinklabs-io/dingo/database/plugin/metadata/sqlite/models"
21
+ "github.com/blinklabs-io/gouroboros/ledger"
22
+ "gorm.io/gorm"
23
+ )
24
+
25
+ // GetUtxo returns a Utxo by reference
26
+ func (d *MetadataStoreSqlite) GetUtxo(
27
+ txId []byte,
28
+ idx uint32,
29
+ txn *gorm.DB,
30
+ ) (models.Utxo, error) {
31
+ ret := models.Utxo{}
32
+ tmpUtxo := models.Utxo{}
33
+ if txn != nil {
34
+ result := txn.Where("deleted_slot = 0").
35
+ First(&tmpUtxo, "tx_id = ? AND output_idx = ?", txId, idx)
36
+ if result.Error != nil {
37
+ return ret, result.Error
38
+ }
39
+ } else {
40
+ result := d.DB().Where("deleted_slot = 0").First(&tmpUtxo, "tx_id = ? AND output_idx = ?", txId, idx)
41
+ if result.Error != nil {
42
+ return ret, result.Error
43
+ }
44
+ }
45
+ ret = tmpUtxo
46
+ return ret, nil
47
+ }
48
+
49
+ // GetUtxosAddedAfterSlot returns a list of Utxos added after a given slot
50
+ func (d *MetadataStoreSqlite) GetUtxosAddedAfterSlot(
51
+ slot uint64,
52
+ txn *gorm.DB,
53
+ ) ([]models.Utxo, error) {
54
+ var ret []models.Utxo
55
+ if txn != nil {
56
+ result := txn.Where("added_slot > 0", slot).
57
+ Order("id DESC").
58
+ Find(&ret)
59
+ if result.Error != nil {
60
+ return ret, result.Error
61
+ }
62
+ } else {
63
+ result := d.DB().Where("added_slot > 0", slot).
64
+ Order("id DESC").
65
+ Find(&ret)
66
+ if result.Error != nil {
67
+ return ret, result.Error
68
+ }
69
+ }
70
+ return ret, nil
71
+ }
72
+
73
+ // GetUtxosDeletedBeforeSlot returns a list of Utxos marked as deleted before a given slot
74
+ func (d *MetadataStoreSqlite) GetUtxosDeletedBeforeSlot(
75
+ slot uint64,
76
+ txn *gorm.DB,
77
+ ) ([]models.Utxo, error) {
78
+ var ret []models.Utxo
79
+ if txn != nil {
80
+ result := txn.Where("deleted_slot > 0 AND deleted_slot <= ?", slot).
81
+ Order("id DESC").
82
+ Find(&ret)
83
+ if result.Error != nil {
84
+ return ret, result.Error
85
+ }
86
+ } else {
87
+ result := d.DB().Where("deleted_slot > 0 AND deleted_slot <= ?", slot).
88
+ Order("id DESC").
89
+ Find(&ret)
90
+ if result.Error != nil {
91
+ return ret, result.Error
92
+ }
93
+ }
94
+ return ret, nil
95
+ }
96
+
97
+ // GetUtxosByAddress returns a list of Utxos
98
+ func (d *MetadataStoreSqlite) GetUtxosByAddress(
99
+ addr ledger.Address,
100
+ txn *gorm.DB,
101
+ ) ([]models.Utxo, error) {
102
+ var ret []models.Utxo
103
+ // Build sub-query for address
104
+ var addrQuery *gorm.DB
105
+ if addr.PaymentKeyHash() != ledger.NewBlake2b224(nil) {
106
+ addrQuery = txn.Where("payment_key = ?", addr.PaymentKeyHash().Bytes())
107
+ }
108
+ if addr.StakeKeyHash() != ledger.NewBlake2b224(nil) {
109
+ if addrQuery != nil {
110
+ addrQuery = addrQuery.Or(
111
+ "staking_key = ?",
112
+ addr.StakeKeyHash().Bytes(),
113
+ )
114
+ } else {
115
+ addrQuery = txn.Where("staking_key = ?", addr.StakeKeyHash().Bytes())
116
+ }
117
+ }
118
+ result := txn.
119
+ Where("deleted_slot = 0").
120
+ Where(addrQuery).
121
+ Find(&ret)
122
+ if result.Error != nil {
123
+ return nil, result.Error
124
+ }
125
+ return ret, nil
126
+ }
127
+
128
+ func (d *MetadataStoreSqlite) DeleteUtxo(
129
+ utxo any,
130
+ txn *gorm.DB,
131
+ ) error {
132
+ tmpUtxo, ok := utxo.(models.Utxo)
133
+ if !ok {
134
+ return fmt.Errorf("failed to convert utxo from %T", utxo)
135
+ }
136
+ if txn != nil {
137
+ result := txn.Delete(&tmpUtxo)
138
+ if result.Error != nil {
139
+ return result.Error
140
+ }
141
+ } else {
142
+ result := d.DB().Delete(&tmpUtxo)
143
+ if result.Error != nil {
144
+ return result.Error
145
+ }
146
+ }
147
+ return nil
148
+ }
149
+
150
+ func (d *MetadataStoreSqlite) DeleteUtxos(
151
+ utxos []any,
152
+ txn *gorm.DB,
153
+ ) error {
154
+ tmpUtxos := []models.Utxo{}
155
+ for _, utxo := range utxos {
156
+ tmpUtxo, ok := utxo.(models.Utxo)
157
+ if !ok {
158
+ return fmt.Errorf("failed to convert utxo from %T", utxo)
159
+ }
160
+ tmpUtxos = append(tmpUtxos, tmpUtxo)
161
+ }
162
+ if txn != nil {
163
+ result := txn.Delete(&tmpUtxos)
164
+ if result.Error != nil {
165
+ return result.Error
166
+ }
167
+ } else {
168
+ result := d.DB().Delete(&tmpUtxos)
169
+ if result.Error != nil {
170
+ return result.Error
171
+ }
172
+ }
173
+ return nil
174
+ }
175
+
176
+ func (d *MetadataStoreSqlite) DeleteUtxosAfterSlot(
177
+ slot uint64,
178
+ txn *gorm.DB,
179
+ ) error {
180
+ if txn != nil {
181
+ result := txn.Where("deleted_slot > ?", slot).
182
+ Delete(&models.Utxo{})
183
+ if result.Error != nil {
184
+ return result.Error
185
+ }
186
+ } else {
187
+ result := d.DB().Where("deleted_slot > ?", slot).
188
+ Delete(&models.Utxo{})
189
+ if result.Error != nil {
190
+ return result.Error
191
+ }
192
+ }
193
+ return nil
194
+ }
195
+
196
+ // DeleteUtxosBeforeSlot deletes Utxos marked as deleted before a given slot
197
+ func (d *MetadataStoreSqlite) DeleteUtxosBeforeSlot(
198
+ slot uint64,
199
+ txn *gorm.DB,
200
+ ) error {
201
+ if txn != nil {
202
+ result := txn.Where("deleted_slot > 0 AND deleted_slot < ?", slot).
203
+ Delete(&models.Utxo{})
204
+ if result.Error != nil {
205
+ return result.Error
206
+ }
207
+ } else {
208
+ result := d.DB().Where("deleted_slot >0 AND deleted_slot < ?", slot).
209
+ Delete(&models.Utxo{})
210
+ if result.Error != nil {
211
+ return result.Error
212
+ }
213
+ }
214
+ return nil
215
+ }
216
+
217
+ // SetUtxo saves a UTxO
218
+ func (d *MetadataStoreSqlite) SetUtxo(
219
+ txId []byte, // hash
220
+ idx uint32, // idx
221
+ slot uint64, // slot
222
+ payment []byte, // payment
223
+ stake []byte, // stake
224
+ txn *gorm.DB,
225
+ ) error {
226
+ tmpUtxo := models.Utxo{
227
+ TxId: txId,
228
+ OutputIdx: idx,
229
+ AddedSlot: slot,
230
+ PaymentKey: payment,
231
+ StakingKey: stake,
232
+ }
233
+ if txn != nil {
234
+ result := txn.Create(&tmpUtxo)
235
+ if result.Error != nil {
236
+ return result.Error
237
+ }
238
+ } else {
239
+ result := d.DB().Create(&tmpUtxo)
240
+ if result.Error != nil {
241
+ return result.Error
242
+ }
243
+ }
244
+ return nil
245
+ }
246
+
247
+ // SetUtxoDeletedAtSlot marks a UTxO as deleted at a given slot
248
+ func (d *MetadataStoreSqlite) SetUtxoDeletedAtSlot(
249
+ utxoId ledger.TransactionInput,
250
+ slot uint64,
251
+ txn *gorm.DB,
252
+ ) error {
253
+ if txn != nil {
254
+ result := txn.Model(models.Utxo{}).
255
+ Where("tx_id = ? AND output_idx = ?", utxoId.Id().Bytes(), utxoId.Index()).
256
+ Update("deleted_slot", slot)
257
+ if result.Error != nil {
258
+ return result.Error
259
+ }
260
+ } else {
261
+ result := d.DB().Model(models.Utxo{}).
262
+ Where("tx_id = ? AND output_idx = ?", utxoId.Id().Bytes(), utxoId.Index()).
263
+ Update("deleted_slot", slot)
264
+ if result.Error != nil {
265
+ return result.Error
266
+ }
267
+ }
268
+ return nil
269
+ }
270
+
271
+ // SetUtxosNotDeletedAfterSlot marks a list of Utxos as not deleted after a given slot
272
+ func (d *MetadataStoreSqlite) SetUtxosNotDeletedAfterSlot(
273
+ slot uint64,
274
+ txn *gorm.DB,
275
+ ) error {
276
+ if txn != nil {
277
+ result := txn.Model(models.Utxo{}).
278
+ Where("deleted_slot > ?", slot).
279
+ Update("deleted_slot", 0)
280
+ if result.Error != nil {
281
+ return result.Error
282
+ }
283
+ } else {
284
+ result := d.DB().Model(models.Utxo{}).
285
+ Where("deleted_slot > ?", slot).
286
+ Update("deleted_slot", 0)
287
+ if result.Error != nil {
288
+ return result.Error
289
+ }
290
+ }
291
+ return nil
292
+ }
@@ -0,0 +1,168 @@
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 metadata
16
+
17
+ import (
18
+ "log/slog"
19
+
20
+ "github.com/blinklabs-io/dingo/database/plugin/metadata/sqlite"
21
+ "github.com/blinklabs-io/dingo/database/plugin/metadata/sqlite/models"
22
+ "github.com/blinklabs-io/gouroboros/ledger"
23
+ lcommon "github.com/blinklabs-io/gouroboros/ledger/common"
24
+ ochainsync "github.com/blinklabs-io/gouroboros/protocol/chainsync"
25
+ "gorm.io/gorm"
26
+ )
27
+
28
+ type MetadataStore interface {
29
+ // Database
30
+ Close() error
31
+ DB() *gorm.DB
32
+ GetCommitTimestamp() (int64, error)
33
+ SetCommitTimestamp(*gorm.DB, int64) error
34
+ Transaction() *gorm.DB
35
+
36
+ // Ledger state
37
+ GetPoolRegistrations(
38
+ lcommon.PoolKeyHash,
39
+ *gorm.DB,
40
+ ) ([]lcommon.PoolRegistrationCertificate, error)
41
+ GetStakeRegistrations(
42
+ []byte, // stakeKey
43
+ *gorm.DB,
44
+ ) ([]lcommon.StakeRegistrationCertificate, error)
45
+ GetTip(*gorm.DB) (ochainsync.Tip, error)
46
+
47
+ GetAccount(
48
+ []byte, // stakeKey
49
+ *gorm.DB,
50
+ ) (models.Account, error)
51
+ GetPParams(
52
+ uint64, // epoch
53
+ *gorm.DB,
54
+ ) ([]models.PParams, error)
55
+ GetPParamUpdates(
56
+ uint64, // epoch
57
+ *gorm.DB,
58
+ ) ([]models.PParamUpdate, error)
59
+ GetUtxo(
60
+ []byte, // txId
61
+ uint32, // idx
62
+ *gorm.DB,
63
+ ) (models.Utxo, error)
64
+
65
+ SetAccount(
66
+ []byte, // stakeKey
67
+ []byte, // pkh
68
+ []byte, // drep
69
+ uint64, // slot
70
+ bool, // active
71
+ *gorm.DB,
72
+ ) error
73
+ SetDeregistration(
74
+ *lcommon.DeregistrationCertificate,
75
+ uint64, // slot
76
+ *gorm.DB,
77
+ ) error
78
+ SetEpoch(
79
+ uint64, // slot
80
+ uint64, // epoch
81
+ []byte, // nonce
82
+ uint, // era
83
+ uint, // slotLength
84
+ uint, // lengthInSlots
85
+ *gorm.DB,
86
+ ) error
87
+ SetPoolRegistration(
88
+ *lcommon.PoolRegistrationCertificate,
89
+ uint64, // slot
90
+ uint64, // deposit
91
+ *gorm.DB,
92
+ ) error
93
+ SetPoolRetirement(
94
+ *lcommon.PoolRetirementCertificate,
95
+ uint64, // slot
96
+ *gorm.DB,
97
+ ) error
98
+ SetPParams(
99
+ []byte, // pparams
100
+ uint64, // slot
101
+ uint64, // epoch
102
+ uint, // era
103
+ *gorm.DB,
104
+ ) error
105
+ SetPParamUpdate(
106
+ []byte, // genesis
107
+ []byte, // update
108
+ uint64, // slot
109
+ uint64, // epoch
110
+ *gorm.DB,
111
+ ) error
112
+ SetRegistration(
113
+ *lcommon.RegistrationCertificate,
114
+ uint64, // slot
115
+ uint64, // deposit
116
+ *gorm.DB,
117
+ ) error
118
+ SetStakeDelegation(
119
+ *lcommon.StakeDelegationCertificate,
120
+ uint64, // slot
121
+ *gorm.DB,
122
+ ) error
123
+ SetStakeDeregistration(
124
+ *lcommon.StakeDeregistrationCertificate,
125
+ uint64, // slot
126
+ *gorm.DB,
127
+ ) error
128
+ SetStakeRegistration(
129
+ *lcommon.StakeRegistrationCertificate,
130
+ uint64, // slot
131
+ uint64, // deposit
132
+ *gorm.DB,
133
+ ) error
134
+ SetTip(
135
+ ochainsync.Tip,
136
+ *gorm.DB,
137
+ ) error
138
+ SetUtxo(
139
+ []byte, // hash
140
+ uint32, // idx
141
+ uint64, // slot
142
+ []byte, // payment
143
+ []byte, // stake
144
+ *gorm.DB,
145
+ ) error
146
+
147
+ // Helpers
148
+ DeleteUtxo(any, *gorm.DB) error
149
+ DeleteUtxos([]any, *gorm.DB) error
150
+ DeleteUtxosAfterSlot(uint64, *gorm.DB) error
151
+ DeleteUtxosBeforeSlot(uint64, *gorm.DB) error
152
+ GetEpochLatest(*gorm.DB) (models.Epoch, error)
153
+ GetEpochsByEra(uint, *gorm.DB) ([]models.Epoch, error)
154
+ GetEpochs(*gorm.DB) ([]models.Epoch, error)
155
+ GetUtxosAddedAfterSlot(uint64, *gorm.DB) ([]models.Utxo, error)
156
+ GetUtxosByAddress(ledger.Address, *gorm.DB) ([]models.Utxo, error)
157
+ GetUtxosDeletedBeforeSlot(uint64, *gorm.DB) ([]models.Utxo, error)
158
+ SetUtxoDeletedAtSlot(ledger.TransactionInput, uint64, *gorm.DB) error
159
+ SetUtxosNotDeletedAfterSlot(uint64, *gorm.DB) error
160
+ }
161
+
162
+ // For now, this always returns a sqlite plugin
163
+ func New(
164
+ pluginName, dataDir string,
165
+ logger *slog.Logger,
166
+ ) (MetadataStore, error) {
167
+ return sqlite.New(dataDir, logger)
168
+ }
@@ -0,0 +1,190 @@
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 plugin
16
+
17
+ import (
18
+ "flag"
19
+ "fmt"
20
+ "os"
21
+ "strconv"
22
+ "strings"
23
+ )
24
+
25
+ type PluginOptionType int
26
+
27
+ const (
28
+ PluginOptionTypeString PluginOptionType = 1
29
+ PluginOptionTypeBool PluginOptionType = 2
30
+ PluginOptionTypeInt PluginOptionType = 3
31
+ PluginOptionTypeUint PluginOptionType = 4
32
+ )
33
+
34
+ type PluginOption struct {
35
+ Name string
36
+ Type PluginOptionType
37
+ CustomEnvVar string
38
+ CustomFlag string
39
+ Description string
40
+ DefaultValue interface{}
41
+ Dest interface{}
42
+ }
43
+
44
+ func (p *PluginOption) AddToFlagSet(
45
+ fs *flag.FlagSet,
46
+ pluginType string,
47
+ pluginName string,
48
+ ) error {
49
+ var flagName string
50
+ if p.CustomFlag != "" {
51
+ flagName = fmt.Sprintf("%s-%s", pluginType, p.CustomFlag)
52
+ } else {
53
+ flagName = fmt.Sprintf("%s-%s-%s", pluginType, pluginName, p.Name)
54
+ }
55
+ switch p.Type {
56
+ case PluginOptionTypeString:
57
+ fs.StringVar(
58
+ p.Dest.(*string),
59
+ flagName,
60
+ p.DefaultValue.(string),
61
+ p.Description,
62
+ )
63
+ case PluginOptionTypeBool:
64
+ fs.BoolVar(
65
+ p.Dest.(*bool),
66
+ flagName,
67
+ p.DefaultValue.(bool),
68
+ p.Description,
69
+ )
70
+ case PluginOptionTypeInt:
71
+ fs.IntVar(p.Dest.(*int), flagName, p.DefaultValue.(int), p.Description)
72
+ case PluginOptionTypeUint:
73
+ fs.UintVar(
74
+ p.Dest.(*uint),
75
+ flagName,
76
+ p.DefaultValue.(uint),
77
+ p.Description,
78
+ )
79
+ default:
80
+ return fmt.Errorf(
81
+ "unknown plugin option type %d for option %s",
82
+ p.Type,
83
+ p.Name,
84
+ )
85
+ }
86
+ return nil
87
+ }
88
+
89
+ func (p *PluginOption) ProcessEnvVars(envPrefix string) error {
90
+ envVars := []string{
91
+ // Automatically generate env var from specified prefix and option name
92
+ strings.ToUpper(
93
+ strings.ReplaceAll(
94
+ fmt.Sprintf(
95
+ "%s%s",
96
+ envPrefix,
97
+ p.Name,
98
+ ),
99
+ "-",
100
+ "_",
101
+ ),
102
+ ),
103
+ }
104
+ // Also check any custom env var specified
105
+ if p.CustomEnvVar != "" {
106
+ envVars = append(envVars, p.CustomEnvVar)
107
+ }
108
+ for _, envVar := range envVars {
109
+ if value, ok := os.LookupEnv(envVar); ok {
110
+ switch p.Type {
111
+ case PluginOptionTypeString:
112
+ *(p.Dest.(*string)) = value
113
+ case PluginOptionTypeBool:
114
+ value, err := strconv.ParseBool(value)
115
+ if err != nil {
116
+ return fmt.Errorf("error processing env vars: %w", err)
117
+ }
118
+ *(p.Dest.(*bool)) = value
119
+ case PluginOptionTypeInt:
120
+ // We limit to 32-bit to not get inconsistent behavior on 32-bit platforms
121
+ value, err := strconv.ParseInt(value, 10, 32)
122
+ if err != nil {
123
+ return fmt.Errorf("error processing env vars: %w", err)
124
+ }
125
+ *(p.Dest.(*int)) = int(value)
126
+ case PluginOptionTypeUint:
127
+ // We limit to 32-bit to not get inconsistent behavior on 32-bit platforms
128
+ value, err := strconv.ParseUint(value, 10, 32)
129
+ if err != nil {
130
+ return fmt.Errorf("error processing env vars: %w", err)
131
+ }
132
+ *(p.Dest.(*uint)) = uint(value)
133
+ default:
134
+ return fmt.Errorf(
135
+ "unknown plugin option type %d for option %s",
136
+ p.Type,
137
+ p.Name,
138
+ )
139
+ }
140
+ }
141
+ }
142
+ return nil
143
+ }
144
+
145
+ func (p *PluginOption) ProcessConfig(
146
+ pluginData map[interface{}]interface{},
147
+ ) error {
148
+ if optionData, ok := pluginData[p.Name]; ok {
149
+ switch p.Type {
150
+ case PluginOptionTypeString:
151
+ switch value := optionData.(type) {
152
+ case string:
153
+ *(p.Dest.(*string)) = value
154
+ default:
155
+ return fmt.Errorf("invalid value for option '%s': expected string and got %T", p.Name, optionData)
156
+ }
157
+ case PluginOptionTypeBool:
158
+ switch value := optionData.(type) {
159
+ case bool:
160
+ *(p.Dest.(*bool)) = value
161
+ default:
162
+ return fmt.Errorf("invalid value for option '%s': expected bool and got %T", p.Name, optionData)
163
+ }
164
+ case PluginOptionTypeInt:
165
+ switch value := optionData.(type) {
166
+ case int:
167
+ *(p.Dest.(*int)) = value
168
+ default:
169
+ return fmt.Errorf("invalid value for option '%s': expected int and got %T", p.Name, optionData)
170
+ }
171
+ case PluginOptionTypeUint:
172
+ switch value := optionData.(type) {
173
+ case int:
174
+ if value < 0 {
175
+ return fmt.Errorf("invalid value for option '%s': negative value: %T", p.Name, optionData)
176
+ }
177
+ *(p.Dest.(*uint)) = uint(value)
178
+ default:
179
+ return fmt.Errorf("invalid value for option '%s': expected uint and got %T", p.Name, optionData)
180
+ }
181
+ default:
182
+ return fmt.Errorf(
183
+ "unknown plugin option type %d for option %s",
184
+ p.Type,
185
+ p.Name,
186
+ )
187
+ }
188
+ }
189
+ return nil
190
+ }
@@ -0,0 +1,20 @@
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 plugin
16
+
17
+ type Plugin interface {
18
+ Start() error
19
+ Stop() error
20
+ }