@blinklabs/dingo 0.19.0 → 0.21.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.
package/README.md CHANGED
@@ -13,7 +13,15 @@
13
13
 
14
14
  ⚠️ This is a work in progress and is currently under heavy development
15
15
 
16
- **Note:** On Windows systems, named pipes are used instead of Unix sockets for node-to-client communication.
16
+ A high-performance Cardano blockchain node implementation in Go by Blink Labs. Dingo provides:
17
+ - Full chain synchronization and validation via Ouroboros consensus protocol
18
+ - UTxO tracking with 41 UTXO validation rules and Plutus smart contract execution
19
+ - Client connectivity for wallets and applications
20
+ - Pluggable storage backends (Badger, SQLite, PostgreSQL, GCS, S3)
21
+ - Peer governance with dynamic peer selection and topology support
22
+ - Chain rollback support for handling forks with automatic state restoration
23
+
24
+ Note: On Windows systems, named pipes are used instead of Unix sockets for node-to-client communication.
17
25
 
18
26
  <div align="center">
19
27
  <img src="./.github/dingo-20241210.png" alt="dingo screenshot" width="640">
@@ -77,6 +85,7 @@ Dingo supports pluggable storage backends for both blob storage (blocks, transac
77
85
 
78
86
  **Metadata Storage Plugins:**
79
87
  - `sqlite` - SQLite relational database (default)
88
+ - `postgres` - PostgreSQL relational database
80
89
 
81
90
  ### Plugin Selection
82
91
 
@@ -112,7 +121,6 @@ Each plugin supports specific configuration options. See `dingo.yaml.example` fo
112
121
  - `bucket` - GCS bucket name
113
122
  - `project-id` - Google Cloud project ID
114
123
  - `prefix` - Path prefix within bucket
115
- - `credentials-file` - Path to service account credentials file (optional - uses Application Default Credentials if not provided)
116
124
 
117
125
  **AWS S3 Options:**
118
126
  - `bucket` - S3 bucket name
@@ -124,6 +132,13 @@ Each plugin supports specific configuration options. See `dingo.yaml.example` fo
124
132
  **SQLite Options:**
125
133
  - `data-dir` - Path to SQLite database file
126
134
 
135
+ **PostgreSQL Options:**
136
+ - `host` - PostgreSQL server hostname
137
+ - `port` - PostgreSQL server port
138
+ - `username` - Database user
139
+ - `password` - Database password
140
+ - `database` - Database name
141
+
127
142
  ### Listing Available Plugins
128
143
 
129
144
  You can see all available plugins and their descriptions:
@@ -165,10 +180,12 @@ testing, so success/failure reports are very welcome and encouraged!
165
180
  - [x] LocalTxMonitor
166
181
  - [x] LocalTxSubmission
167
182
  - [x] LocalStateQuery
168
- - [ ] Peer governor
183
+ - [x] Peer governor
169
184
  - [x] Topology config
170
- - [ ] Peer churn
171
- - [ ] Ledger peers
185
+ - [x] Peer churn (full PeerChurnEvent with gossip/public root churn, bootstrap events)
186
+ - [x] Ledger peers
187
+ - [x] Peer sharing
188
+ - [x] Denied peers tracking
172
189
  - [x] Connection manager
173
190
  - [x] Inbound connections
174
191
  - [x] Node-to-client over TCP
@@ -179,12 +196,14 @@ testing, so success/failure reports are very welcome and encouraged!
179
196
  - [ ] Ledger
180
197
  - [x] Blocks
181
198
  - [x] Block storage
182
- - [ ] Chain selection
199
+ - [x] Chain selection (density comparison, VRF tie-breaker, ChainForkEvent)
183
200
  - [x] UTxO tracking
184
201
  - [x] Protocol parameters
202
+ - [x] Genesis validation
185
203
  - [ ] Certificates
186
204
  - [x] Pool registration
187
205
  - [x] Stake registration/delegation
206
+ - [x] Account registration checks
188
207
  - [ ] Governance
189
208
  - [ ] Transaction validation
190
209
  - [ ] Phase 1 validation
@@ -202,6 +221,14 @@ testing, so success/failure reports are very welcome and encouraged!
202
221
  - [x] Validation of transaction on add
203
222
  - [x] Consumer tracking
204
223
  - [x] Transaction purging on chain update
224
+ - [x] Database Recovery
225
+ - [x] Chain rollback support (SQLite and PostgreSQL plugins)
226
+ - [x] State restoration on rollback
227
+ - [x] WAL mode for crash recovery
228
+ - [x] Automatic rollback on transaction error
229
+ - [x] Plutus Validation
230
+ - [x] Plutus V3 smart contract validation
231
+ - [ ] Plutus V1/V2 smart contract validation
205
232
 
206
233
  Additional planned features can be found in our issue tracker and project boards.
207
234
 
@@ -32,8 +32,6 @@ database:
32
32
  project-id: ""
33
33
  # Path prefix within the bucket
34
34
  prefix: ""
35
- # Path to service account credentials file (optional - uses Application Default Credentials if not set)
36
- credentials-file: ""
37
35
  s3:
38
36
  # AWS S3 bucket name
39
37
  bucket: ""
@@ -48,12 +46,50 @@ database:
48
46
 
49
47
  # Metadata storage plugin configuration
50
48
  metadata:
51
- # Plugin to use for metadata storage (sqlite)
49
+ # Plugin to use for metadata storage (sqlite, postgres, mysql)
52
50
  plugin: "sqlite"
53
51
  # Configuration options for each plugin
54
52
  sqlite:
55
- # Data directory for SQLite database file
53
+ # Path to SQLite database file
56
54
  data-dir: ".dingo/metadata.db"
55
+ postgres:
56
+ # NOTE: These are example values for local development only.
57
+ # Do not use these credentials in production environments.
58
+ # Postgres host
59
+ host: "localhost"
60
+ # Postgres port
61
+ port: 5432
62
+ # Postgres user
63
+ user: "postgres"
64
+ # Postgres password (required - no default)
65
+ password: ""
66
+ # Postgres database name
67
+ database: "postgres"
68
+ # Postgres sslmode
69
+ ssl-mode: "disable"
70
+ # Postgres TimeZone
71
+ timezone: "UTC"
72
+ # Full Postgres DSN (overrides other options when set)
73
+ dsn: ""
74
+ mysql:
75
+ # NOTE: These are example values for local development only.
76
+ # Do not use these credentials in production environments.
77
+ # MySQL host
78
+ host: "localhost"
79
+ # MySQL port
80
+ port: 3306
81
+ # MySQL user
82
+ user: "root"
83
+ # MySQL password (required - no default)
84
+ password: ""
85
+ # MySQL database name
86
+ database: "mysql"
87
+ # MySQL TLS mode (mapped to tls= in DSN)
88
+ ssl-mode: ""
89
+ # MySQL time zone location
90
+ timezone: "UTC"
91
+ # Full MySQL DSN (overrides other options when set)
92
+ dsn: ""
57
93
 
58
94
  # Path to the UNIX domain socket file used by the server
59
95
  socketPath: "dingo.socket"
@@ -100,9 +136,44 @@ intersectTip: false
100
136
  # Default: 1048576 (1 MB)
101
137
  mempoolCapacity: 1048576
102
138
 
103
- # Enable development mode which prevents outbound connections
104
- # Default: false
105
- devMode: false
139
+ # Operational mode: "serve" (default), "load", or "dev"
140
+ # - serve: Full node with network connectivity (default)
141
+ # - load: Batch import from ImmutableDB (requires immutableDbPath)
142
+ # - dev: Development mode (forge blocks, disable outbound, skip topology)
143
+ # Note: CLI commands (serve, load) take priority over this setting
144
+ #
145
+ # Can be overridden with the DINGO_RUN_MODE environment variable
146
+ runMode: "serve"
147
+
148
+ # Path to ImmutableDB for batch import (used when runMode is "load")
149
+ # Can also be provided as argument to 'dingo load' command
150
+ #
151
+ # Can be overridden with the DINGO_IMMUTABLE_DB_PATH environment variable
152
+ immutableDbPath: ""
106
153
 
107
154
  # Validate historical blocks during ledger processing (default: false)
108
155
  validateHistorical: false
156
+
157
+ # Peer targets - target number of peers in each state
158
+ # These are goals the system works toward, not hard limits.
159
+ # Use 0 for default values, -1 for unlimited
160
+ # Default: known=150, established=50, active=20
161
+ #
162
+ # Target number of known (cold) peers
163
+ targetNumberOfKnownPeers: 0
164
+ # Target number of established (warm) peers
165
+ targetNumberOfEstablishedPeers: 0
166
+ # Target number of active (hot) peers
167
+ targetNumberOfActivePeers: 0
168
+
169
+ # Per-source quotas for active peers
170
+ # These specify how active peer slots are distributed by source.
171
+ # Use 0 for default values.
172
+ # Default: topology=3, gossip=12, ledger=5
173
+ #
174
+ # Active peer slots for topology sources (local + public roots)
175
+ activePeersTopologyQuota: 0
176
+ # Active peer slots for gossip sources
177
+ activePeersGossipQuota: 0
178
+ # Active peer slots for ledger sources
179
+ activePeersLedgerQuota: 0
@@ -281,10 +281,10 @@ $benchmark"
281
281
  done < <(list_previous_benchmarks)
282
282
 
283
283
  # Count items (remove empty lines and count)
284
- faster_count=$(echo "$faster_benchmarks" | grep -c "^." || echo "0")
285
- slower_count=$(echo "$slower_benchmarks" | grep -c "^." || echo "0")
286
- new_count=$(echo "$new_benchmarks" | grep -c "^." || echo "0")
287
- removed_count=$(echo "$removed_benchmarks" | grep -c "^." || echo "0")
284
+ faster_count=$(echo "$faster_benchmarks" | sed '/^$/d' | wc -l)
285
+ slower_count=$(echo "$slower_benchmarks" | sed '/^$/d' | wc -l)
286
+ new_count=$(echo "$new_benchmarks" | sed '/^$/d' | wc -l)
287
+ removed_count=$(echo "$removed_benchmarks" | sed '/^$/d' | wc -l)
288
288
 
289
289
  echo ""
290
290
  echo "Performance Changes Summary:"
@@ -362,10 +362,10 @@ $benchmark"
362
362
  done < <(list_previous_benchmarks)
363
363
 
364
364
  # Count items
365
- faster_count=$(echo "$faster_benchmarks" | grep -c "^." || echo "0")
366
- slower_count=$(echo "$slower_benchmarks" | grep -c "^." || echo "0")
367
- new_count=$(echo "$new_benchmarks" | grep -c "^." || echo "0")
368
- removed_count=$(echo "$removed_benchmarks" | grep -c "^." || echo "0")
365
+ faster_count=$(echo "$faster_benchmarks" | sed '/^$/d' | wc -l)
366
+ slower_count=$(echo "$slower_benchmarks" | sed '/^$/d' | wc -l)
367
+ new_count=$(echo "$new_benchmarks" | sed '/^$/d' | wc -l)
368
+ removed_count=$(echo "$removed_benchmarks" | sed '/^$/d' | wc -l)
369
369
 
370
370
  echo "### Summary"
371
371
  echo "- **Faster benchmarks**: $faster_count"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blinklabs/dingo",
3
- "version": "0.19.0",
3
+ "version": "0.21.0",
4
4
  "description": "Dingo is a Cardano blockchain data node",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -1,306 +0,0 @@
1
- # Plugin Development Guide
2
-
3
- This guide explains how to develop plugins for Dingo's storage system.
4
-
5
- ## Overview
6
-
7
- Dingo supports pluggable storage backends through a registration-based plugin system. Plugins can extend the system with new blob storage (blocks, transactions) and metadata storage (indexes, state) implementations.
8
-
9
- ## Plugin Types
10
-
11
- ### Blob Storage Plugins
12
- Store blockchain data (blocks, transactions, etc.). Examples:
13
- - `badger` - Local BadgerDB key-value store
14
- - `gcs` - Google Cloud Storage
15
- - `s3` - AWS S3
16
-
17
- #### Iterator lifetime note
18
- Blob plugins expose iterators via `NewIterator(txn, opts)`. Items returned by the
19
- iterator's `Item()` must only be accessed while the transaction used to create
20
- the iterator is still active — implementations may validate transaction state at
21
- access time and will return errors if the transaction has been committed or
22
- rolled back. See `database/types/types.go` `BlobIterator` for details.
23
-
24
- ### Metadata Storage Plugins
25
- Store metadata and indexes. Examples:
26
- - `sqlite` - SQLite relational database
27
-
28
- ## Plugin Interface
29
-
30
- All plugins must implement the `plugin.Plugin` interface:
31
-
32
- ```go
33
- type Plugin interface {
34
- Start() error
35
- Stop() error
36
- }
37
- ```
38
-
39
- ## Plugin Registration
40
-
41
- Plugins register themselves during package initialization using the `plugin.Register()` function:
42
-
43
- ```go
44
- func init() {
45
- plugin.Register(plugin.PluginEntry{
46
- Type: plugin.PluginTypeBlob, // or PluginTypeMetadata
47
- Name: "myplugin",
48
- Description: "My custom storage plugin",
49
- NewFromOptionsFunc: NewFromCmdlineOptions,
50
- Options: []plugin.PluginOption{
51
- // Plugin-specific options
52
- },
53
- })
54
- }
55
- ```
56
-
57
- ## Plugin Options
58
-
59
- Plugins define configuration options using the `PluginOption` struct:
60
-
61
- ```go
62
- plugin.PluginOption{
63
- Name: "data-dir", // Option name
64
- Type: plugin.PluginOptionTypeString, // Data type
65
- Description: "Data directory path", // Help text
66
- DefaultValue: "/tmp/data", // Default value
67
- Dest: &cmdlineOptions.dataDir, // Destination variable
68
- }
69
- ```
70
-
71
- Supported option types:
72
- - `PluginOptionTypeString`
73
- - `PluginOptionTypeBool`
74
- - `PluginOptionTypeInt`
75
- - `PluginOptionTypeUint`
76
-
77
- ## Environment Variables
78
-
79
- Plugins automatically support environment variables with the pattern:
80
- `DINGO_DATABASE_{TYPE}_{PLUGIN}_{OPTION}`
81
-
82
- Examples:
83
- - `DINGO_DATABASE_BLOB_BADGER_DATA_DIR=/data`
84
- - `DINGO_DATABASE_METADATA_SQLITE_DATA_DIR=/metadata.db`
85
-
86
- ## YAML Configuration
87
-
88
- Plugins can be configured in `dingo.yaml`:
89
-
90
- ```yaml
91
- database:
92
- blob:
93
- plugin: "myplugin"
94
- myplugin:
95
- option1: "value1"
96
- option2: 42
97
- metadata:
98
- plugin: "sqlite"
99
- sqlite:
100
- data-dir: "/data/metadata.db"
101
- ```
102
-
103
- ## Configuration Precedence
104
-
105
- 1. Command-line flags (highest priority)
106
- 2. Environment variables
107
- 3. YAML configuration
108
- 4. Default values (lowest priority)
109
-
110
- ## Command Line Options
111
-
112
- Plugins support command-line flags with the pattern:
113
- `--{type}-{plugin}-{option}`
114
-
115
- Examples:
116
- - `--blob-badger-data-dir /data`
117
- - `--metadata-sqlite-data-dir /metadata.db`
118
-
119
- ## Plugin Development Steps
120
-
121
- ### 1. Create Plugin Structure
122
-
123
- ```text
124
- database/plugin/{type}/{name}/
125
- ├── plugin.go # Registration and options
126
- ├── options.go # Option functions
127
- ├── database.go # Core implementation
128
- └── options_test.go # Unit tests
129
- ```
130
-
131
- ### 2. Implement Core Plugin
132
-
133
- Create the main plugin struct that implements `plugin.Plugin`:
134
-
135
- ```go
136
- type MyPlugin struct {
137
- // Fields
138
- }
139
-
140
- func (p *MyPlugin) Start() error {
141
- // Initialize resources
142
- return nil
143
- }
144
-
145
- func (p *MyPlugin) Stop() error {
146
- // Clean up resources
147
- return nil
148
- }
149
- ```
150
-
151
- ### 3. Define Options
152
-
153
- Create option functions following the pattern:
154
-
155
- ```go
156
- func WithOptionName(value Type) OptionFunc {
157
- return func(p *MyPlugin) {
158
- p.field = value
159
- }
160
- }
161
- ```
162
-
163
- ### 4. Implement Constructors
164
-
165
- Provide both options-based and legacy constructors:
166
-
167
- ```go
168
- func NewWithOptions(opts ...OptionFunc) (*MyPlugin, error) {
169
- p := &MyPlugin{}
170
- for _, opt := range opts {
171
- opt(p)
172
- }
173
- return p, nil
174
- }
175
-
176
- func New(legacyParam1, legacyParam2) (*MyPlugin, error) {
177
- // For backward compatibility
178
- return NewWithOptions(
179
- WithOption1(legacyParam1),
180
- WithOption2(legacyParam2),
181
- )
182
- }
183
- ```
184
-
185
- ### 5. Register Plugin
186
-
187
- In `plugin.go`, register during initialization:
188
-
189
- ```go
190
- var cmdlineOptions struct {
191
- option1 string
192
- option2 int
193
- }
194
-
195
- func init() {
196
- plugin.Register(plugin.PluginEntry{
197
- Type: plugin.PluginTypeBlob,
198
- Name: "myplugin",
199
- Description: "My custom plugin",
200
- NewFromOptionsFunc: NewFromCmdlineOptions,
201
- Options: []plugin.PluginOption{
202
- {
203
- Name: "option1",
204
- Type: plugin.PluginOptionTypeString,
205
- Description: "First option",
206
- DefaultValue: "default",
207
- Dest: &cmdlineOptions.option1,
208
- },
209
- // More options...
210
- },
211
- })
212
- }
213
-
214
- func NewFromCmdlineOptions() plugin.Plugin {
215
- p, err := NewWithOptions(
216
- WithOption1(cmdlineOptions.option1),
217
- WithOption2(cmdlineOptions.option2),
218
- )
219
- if err != nil {
220
- panic(err)
221
- }
222
- return p
223
- }
224
- ```
225
-
226
- ### 6. Add Tests
227
-
228
- Create comprehensive tests:
229
-
230
- ```go
231
- func TestOptions(t *testing.T) {
232
- // Test option functions
233
- }
234
-
235
- func TestLifecycle(t *testing.T) {
236
- p, err := NewWithOptions(WithOption1("test"))
237
- // Test Start/Stop
238
- }
239
- ```
240
-
241
- ### 7. Update Imports
242
-
243
- Add your plugin to the import list in the appropriate store file:
244
- - `database/plugin/blob/blob.go` for blob plugins
245
- - `database/plugin/metadata/metadata.go` for metadata plugins
246
-
247
- ## Example: Complete Plugin
248
-
249
- See the existing plugins for complete examples:
250
- - `database/plugin/blob/badger/` - BadgerDB implementation
251
- - `database/plugin/metadata/sqlite/` - SQLite implementation
252
- - `database/plugin/blob/gcs/` - Google Cloud Storage implementation
253
- - `database/plugin/blob/aws/` - AWS S3 implementation
254
-
255
- ## Best Practices
256
-
257
- 1. **Error Handling**: Always return descriptive errors
258
- 2. **Resource Management**: Properly implement Start/Stop for resource lifecycle
259
- 3. **Thread Safety**: Ensure plugins are safe for concurrent use
260
- 4. **Configuration Validation**: Validate configuration during construction
261
- 5. **Backward Compatibility**: Maintain compatibility with existing deployments
262
- 6. **Documentation**: Document all options and their effects
263
- 7. **Testing**: Provide comprehensive unit and integration tests
264
-
265
- ## Testing Your Plugin
266
-
267
- ### Unit Tests
268
- Test individual components and option functions.
269
-
270
- ### Integration Tests
271
- Test the complete plugin lifecycle and interaction with the plugin system.
272
-
273
- ### CLI Testing
274
- Use the CLI to test plugin listing and selection:
275
-
276
- ```bash
277
- ./dingo --blob list
278
- ./dingo --metadata list
279
- ```
280
-
281
- ### Configuration Testing
282
- Test environment variables and YAML configuration:
283
-
284
- ```bash
285
- DINGO_DATABASE_BLOB_MYPLUGIN_OPTION1=value ./dingo --blob myplugin
286
- ```
287
-
288
- ## Programmatic Option Overrides (for tests)
289
-
290
- When writing tests or programmatically constructing database instances you can override plugin options
291
- without importing plugin implementation packages directly by using the plugin registry helper:
292
-
293
- ```go
294
- // Set data-dir for the blob plugin to a per-test temp directory
295
- plugin.SetPluginOption(plugin.PluginTypeBlob, "badger", "data-dir", t.TempDir())
296
-
297
- // Set data-dir for the metadata plugin
298
- plugin.SetPluginOption(plugin.PluginTypeMetadata, "sqlite", "data-dir", t.TempDir())
299
- ```
300
-
301
- The helper sets the plugin option's destination variable in the registry before plugin instantiation.
302
- If the requested option is not defined by the targeted plugin the call is non-fatal and returns nil,
303
- allowing tests to run regardless of which plugin implementation is selected.
304
-
305
- Using `t.TempDir()` guarantees each test uses its own on-disk path and prevents concurrent tests from
306
- colliding on shared directories (for example the default `.dingo` Badger directory).