@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,145 @@
1
+ // Copyright 2024 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 config
16
+
17
+ import (
18
+ "context"
19
+ "fmt"
20
+ "os"
21
+
22
+ "github.com/blinklabs-io/dingo/topology"
23
+ ouroboros "github.com/blinklabs-io/gouroboros"
24
+ "github.com/kelseyhightower/envconfig"
25
+ "gopkg.in/yaml.v3"
26
+ )
27
+
28
+ type ctxKey string
29
+
30
+ const configContextKey ctxKey = "dingo.config"
31
+
32
+ func WithContext(ctx context.Context, cfg *Config) context.Context {
33
+ return context.WithValue(ctx, configContextKey, cfg)
34
+ }
35
+
36
+ func FromContext(ctx context.Context) *Config {
37
+ cfg, ok := ctx.Value(configContextKey).(*Config)
38
+ if !ok {
39
+ return nil
40
+ }
41
+ return cfg
42
+ }
43
+
44
+ type Config struct {
45
+ BindAddr string `yaml:"bindAddr" split_words:"true"`
46
+ CardanoConfig string `yaml:"cardanoConfig" envconfig:"config"`
47
+ DatabasePath string `yaml:"databasePath" split_words:"true"`
48
+ SocketPath string `yaml:"socketPath" split_words:"true"`
49
+ Network string `yaml:"network"`
50
+ TlsCertFilePath string `yaml:"tlsCertFilePath" envconfig:"TLS_CERT_FILE_PATH"`
51
+ TlsKeyFilePath string `yaml:"tlsKeyFilePath" envconfig:"TLS_KEY_FILE_PATH"`
52
+ Topology string `yaml:"topology"`
53
+ MetricsPort uint `yaml:"metricsPort" split_words:"true"`
54
+ PrivateBindAddr string `yaml:"privateBindAddr" split_words:"true"`
55
+ PrivatePort uint `yaml:"privatePort" split_words:"true"`
56
+ RelayPort uint `yaml:"relayPort" envconfig:"port"`
57
+ UtxorpcPort uint `yaml:"utxorpcPort" split_words:"true"`
58
+ IntersectTip bool `yaml:"intersectTip" split_words:"true"`
59
+ }
60
+
61
+ var globalConfig = &Config{
62
+ BindAddr: "0.0.0.0",
63
+ CardanoConfig: "./config/cardano/preview/config.json",
64
+ DatabasePath: ".dingo",
65
+ SocketPath: "dingo.socket",
66
+ IntersectTip: false,
67
+ Network: "preview",
68
+ MetricsPort: 12798,
69
+ PrivateBindAddr: "127.0.0.1",
70
+ PrivatePort: 3002,
71
+ RelayPort: 3001,
72
+ UtxorpcPort: 9090,
73
+ Topology: "",
74
+ TlsCertFilePath: "",
75
+ TlsKeyFilePath: "",
76
+ }
77
+
78
+ func LoadConfig(configFile string) (*Config, error) {
79
+ // Load config file as YAML if provided
80
+ fmt.Println(configFile)
81
+ if configFile != "" {
82
+ fmt.Printf("Reading from config file %s\n", configFile)
83
+ buf, err := os.ReadFile(configFile)
84
+ if err != nil {
85
+ return nil, fmt.Errorf("error reading config file: %w", err)
86
+ }
87
+ err = yaml.Unmarshal(buf, globalConfig)
88
+ if err != nil {
89
+ return nil, fmt.Errorf("error parsing config file: %w", err)
90
+ }
91
+ fmt.Printf("Successfully loaded the config file %s\n", configFile)
92
+ }
93
+ err := envconfig.Process("cardano", globalConfig)
94
+ if err != nil {
95
+ return nil, fmt.Errorf("error processing environment: %+w", err)
96
+ }
97
+ _, err = LoadTopologyConfig()
98
+ if err != nil {
99
+ return nil, fmt.Errorf("error loading topology: %+w", err)
100
+ }
101
+ return globalConfig, nil
102
+ }
103
+
104
+ func GetConfig() *Config {
105
+ return globalConfig
106
+ }
107
+
108
+ var globalTopologyConfig = &topology.TopologyConfig{}
109
+
110
+ func LoadTopologyConfig() (*topology.TopologyConfig, error) {
111
+ if globalConfig.Topology == "" {
112
+ // Use default bootstrap peers for specified network
113
+ network, ok := ouroboros.NetworkByName(globalConfig.Network)
114
+ if !ok {
115
+ return nil, fmt.Errorf("unknown network: %s", globalConfig.Network)
116
+ }
117
+ if len(network.BootstrapPeers) == 0 {
118
+ return nil, fmt.Errorf(
119
+ "no known bootstrap peers for network %s",
120
+ globalConfig.Network,
121
+ )
122
+ }
123
+ for _, peer := range network.BootstrapPeers {
124
+ globalTopologyConfig.BootstrapPeers = append(
125
+ globalTopologyConfig.BootstrapPeers,
126
+ topology.TopologyConfigP2PAccessPoint{
127
+ Address: peer.Address,
128
+ Port: peer.Port,
129
+ },
130
+ )
131
+ }
132
+ return globalTopologyConfig, nil
133
+ }
134
+ tc, err := topology.NewTopologyConfigFromFile(globalConfig.Topology)
135
+ if err != nil {
136
+ return nil, fmt.Errorf("failed to load topology file: %+w", err)
137
+ }
138
+ // update globalTopologyConfig
139
+ globalTopologyConfig = tc
140
+ return globalTopologyConfig, nil
141
+ }
142
+
143
+ func GetTopologyConfig() *topology.TopologyConfig {
144
+ return globalTopologyConfig
145
+ }
@@ -0,0 +1,118 @@
1
+ package config
2
+
3
+ import (
4
+ "os"
5
+ "reflect"
6
+ "testing"
7
+ )
8
+
9
+ func resetGlobalConfig() {
10
+ globalConfig = &Config{
11
+ BindAddr: "0.0.0.0",
12
+ CardanoConfig: "./config/cardano/preview/config.json",
13
+ DatabasePath: ".dingo",
14
+ SocketPath: "dingo.socket",
15
+ IntersectTip: false,
16
+ Network: "preview",
17
+ MetricsPort: 12798,
18
+ PrivateBindAddr: "127.0.0.1",
19
+ PrivatePort: 3002,
20
+ RelayPort: 3001,
21
+ UtxorpcPort: 9090,
22
+ Topology: "",
23
+ TlsCertFilePath: "",
24
+ TlsKeyFilePath: "",
25
+ }
26
+ }
27
+
28
+ func TestLoad_CompareFullStruct(t *testing.T) {
29
+ resetGlobalConfig()
30
+ yamlContent := `
31
+ bindAddr: "127.0.0.1"
32
+ cardanoConfig: "./cardano/preview/config.json"
33
+ databasePath: ".dingo"
34
+ socketPath: "env.socket"
35
+ intersectTip: true
36
+ network: "preview"
37
+ metricsPort: 8088
38
+ privateBindAddr: "127.0.0.1"
39
+ privatePort: 8000
40
+ relayPort: 4000
41
+ utxorpcPort: 9940
42
+ topology: ""
43
+ tlsCertFilePath: "cert1.pem"
44
+ tlsKeyFilePath: "key1.pem"
45
+ `
46
+
47
+ tmpFile := "test-dingo.yaml"
48
+ err := os.WriteFile(tmpFile, []byte(yamlContent), 0644)
49
+ if err != nil {
50
+ t.Fatalf("failed to write config file: %v", err)
51
+ }
52
+ defer os.Remove(tmpFile)
53
+
54
+ expected := &Config{
55
+ BindAddr: "127.0.0.1",
56
+ CardanoConfig: "./cardano/preview/config.json",
57
+ DatabasePath: ".dingo",
58
+ SocketPath: "env.socket",
59
+ IntersectTip: true,
60
+ Network: "preview",
61
+ MetricsPort: 8088,
62
+ PrivateBindAddr: "127.0.0.1",
63
+ PrivatePort: 8000,
64
+ RelayPort: 4000,
65
+ UtxorpcPort: 9940,
66
+ Topology: "",
67
+ TlsCertFilePath: "cert1.pem",
68
+ TlsKeyFilePath: "key1.pem",
69
+ }
70
+
71
+ actual, err := LoadConfig(tmpFile)
72
+ if err != nil {
73
+ t.Fatalf("failed to load config: %v", err)
74
+ }
75
+
76
+ if !reflect.DeepEqual(actual, expected) {
77
+ t.Errorf(
78
+ "Loaded config does not match expected.\nActual: %+v\nExpected: %+v",
79
+ actual,
80
+ expected,
81
+ )
82
+ }
83
+ }
84
+ func TestLoad_WithoutConfigFile_UsesDefaults(t *testing.T) {
85
+ resetGlobalConfig()
86
+
87
+ // Without Config file
88
+ cfg, err := LoadConfig("")
89
+ if err != nil {
90
+ t.Fatalf("expected no error, got: %v", err)
91
+ }
92
+
93
+ // Expected is the original default values from globalConfig
94
+ expected := &Config{
95
+ BindAddr: "0.0.0.0",
96
+ CardanoConfig: "./config/cardano/preview/config.json",
97
+ DatabasePath: ".dingo",
98
+ SocketPath: "dingo.socket",
99
+ IntersectTip: false,
100
+ Network: "preview",
101
+ MetricsPort: 12798,
102
+ PrivateBindAddr: "127.0.0.1",
103
+ PrivatePort: 3002,
104
+ RelayPort: 3001,
105
+ UtxorpcPort: 9090,
106
+ Topology: "",
107
+ TlsCertFilePath: "",
108
+ TlsKeyFilePath: "",
109
+ }
110
+
111
+ if !reflect.DeepEqual(cfg, expected) {
112
+ t.Errorf(
113
+ "config mismatch without file:\nExpected: %+v\nGot: %+v",
114
+ expected,
115
+ cfg,
116
+ )
117
+ }
118
+ }
@@ -0,0 +1,149 @@
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 node
16
+
17
+ import (
18
+ "fmt"
19
+ "log/slog"
20
+ "slices"
21
+ "time"
22
+
23
+ "github.com/blinklabs-io/dingo/config/cardano"
24
+ "github.com/blinklabs-io/dingo/database/immutable"
25
+ "github.com/blinklabs-io/dingo/event"
26
+ "github.com/blinklabs-io/dingo/internal/config"
27
+ "github.com/blinklabs-io/dingo/ledger"
28
+ gledger "github.com/blinklabs-io/gouroboros/ledger"
29
+ )
30
+
31
+ func Load(cfg *config.Config, logger *slog.Logger, immutableDir string) error {
32
+ var nodeCfg *cardano.CardanoNodeConfig
33
+ if cfg.CardanoConfig != "" {
34
+ tmpCfg, err := cardano.NewCardanoNodeConfigFromFile(cfg.CardanoConfig)
35
+ if err != nil {
36
+ return err
37
+ }
38
+ nodeCfg = tmpCfg
39
+ logger.Debug(
40
+ fmt.Sprintf(
41
+ "cardano network config: %+v",
42
+ nodeCfg,
43
+ ),
44
+ "component", "node",
45
+ )
46
+ }
47
+ // Load state
48
+ eventBus := event.NewEventBus(nil)
49
+ ls, err := ledger.NewLedgerState(
50
+ ledger.LedgerStateConfig{
51
+ DataDir: cfg.DatabasePath,
52
+ Logger: logger,
53
+ CardanoNodeConfig: nodeCfg,
54
+ EventBus: eventBus,
55
+ },
56
+ )
57
+ if err != nil {
58
+ return fmt.Errorf("failed to load state database: %w", err)
59
+ }
60
+ c := ls.Chain()
61
+ // Open immutable DB
62
+ immutable, err := immutable.New(immutableDir)
63
+ if err != nil {
64
+ return fmt.Errorf("failed to read immutable DB: %w", err)
65
+ }
66
+ // Record immutable DB tip
67
+ immutableTip, err := immutable.GetTip()
68
+ if err != nil {
69
+ return fmt.Errorf("failed to read immutable DB tip: %w", err)
70
+ }
71
+ // Copy all blocks
72
+ logger.Info("copying blocks from immutable DB")
73
+ chainTip := c.Tip()
74
+ iter, err := immutable.BlocksFromPoint(chainTip.Point)
75
+ if err != nil {
76
+ return fmt.Errorf("failed to get immutable DB iterator: %w", err)
77
+ }
78
+ var blocksCopied int
79
+ blockBatch := make([]gledger.Block, 0, 500)
80
+ for {
81
+ for {
82
+ next, err := iter.Next()
83
+ if err != nil {
84
+ return err
85
+ }
86
+ // No more blocks
87
+ if next == nil {
88
+ break
89
+ }
90
+ tmpBlock, err := gledger.NewBlockFromCbor(next.Type, next.Cbor)
91
+ if err != nil {
92
+ return err
93
+ }
94
+ // Skip first block when continuing a load operation
95
+ if blocksCopied == 0 &&
96
+ tmpBlock.SlotNumber() == chainTip.Point.Slot {
97
+ continue
98
+ }
99
+ blockBatch = append(blockBatch, tmpBlock)
100
+ if len(blockBatch) == cap(blockBatch) {
101
+ break
102
+ }
103
+ }
104
+ if len(blockBatch) == 0 {
105
+ break
106
+ }
107
+ // Add block batch to chain
108
+ if err := c.AddBlocks(blockBatch); err != nil {
109
+ logger.Error(
110
+ fmt.Sprintf(
111
+ "failed to import block: %s",
112
+ err,
113
+ ),
114
+ )
115
+ return nil
116
+ }
117
+ blocksCopied += len(blockBatch)
118
+ blockBatch = slices.Delete(blockBatch, 0, len(blockBatch))
119
+ if blocksCopied > 0 && blocksCopied%10000 == 0 {
120
+ logger.Info(
121
+ fmt.Sprintf(
122
+ "copying blocks from immutable DB (%d blocks copied)",
123
+ blocksCopied,
124
+ ),
125
+ )
126
+ }
127
+ }
128
+ logger.Info(
129
+ fmt.Sprintf(
130
+ "finished copying %d blocks from immutable DB",
131
+ blocksCopied,
132
+ ),
133
+ )
134
+ // Wait for ledger to catch up
135
+ for {
136
+ time.Sleep(5 * time.Second)
137
+ tip := ls.Tip()
138
+ if tip.Point.Slot >= immutableTip.Slot {
139
+ break
140
+ }
141
+ }
142
+ logger.Info(
143
+ fmt.Sprintf(
144
+ "finished processing %d blocks from immutable DB",
145
+ blocksCopied,
146
+ ),
147
+ )
148
+ return nil
149
+ }
@@ -0,0 +1,176 @@
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 node
16
+
17
+ import (
18
+ "context"
19
+ "fmt"
20
+ "log/slog"
21
+ "net/http"
22
+ _ "net/http/pprof" // #nosec G108
23
+ "os"
24
+ "os/signal"
25
+ "syscall"
26
+ "time"
27
+
28
+ "github.com/blinklabs-io/dingo"
29
+ "github.com/blinklabs-io/dingo/config/cardano"
30
+ "github.com/blinklabs-io/dingo/internal/config"
31
+ "github.com/prometheus/client_golang/prometheus"
32
+ "github.com/prometheus/client_golang/prometheus/promhttp"
33
+ )
34
+
35
+ func Run(cfg *config.Config, logger *slog.Logger) error {
36
+ logger.Debug(fmt.Sprintf("config: %+v", cfg), "component", "node")
37
+ logger.Debug(
38
+ fmt.Sprintf("topology: %+v", config.GetTopologyConfig()),
39
+ "component", "node",
40
+ )
41
+ // TODO: make this safer, check PID, create parent, etc. (#276)
42
+ if _, err := os.Stat(cfg.SocketPath); err == nil {
43
+ os.Remove(cfg.SocketPath)
44
+ }
45
+ var nodeCfg *cardano.CardanoNodeConfig
46
+ if cfg.CardanoConfig != "" {
47
+ tmpCfg, err := cardano.NewCardanoNodeConfigFromFile(cfg.CardanoConfig)
48
+ if err != nil {
49
+ return err
50
+ }
51
+ nodeCfg = tmpCfg
52
+ logger.Debug(
53
+ fmt.Sprintf(
54
+ "cardano network config: %+v",
55
+ nodeCfg,
56
+ ),
57
+ "component", "node",
58
+ )
59
+ }
60
+ listeners := []dingo.ListenerConfig{}
61
+ if cfg.RelayPort > 0 {
62
+ // Public "relay" port (node-to-node)
63
+ listeners = append(
64
+ listeners,
65
+ dingo.ListenerConfig{
66
+ ListenNetwork: "tcp",
67
+ ListenAddress: fmt.Sprintf(
68
+ "%s:%d",
69
+ cfg.BindAddr,
70
+ cfg.RelayPort,
71
+ ),
72
+ ReuseAddress: true,
73
+ },
74
+ )
75
+ }
76
+ if cfg.PrivatePort > 0 {
77
+ // Private TCP port (node-to-client)
78
+ listeners = append(
79
+ listeners,
80
+ dingo.ListenerConfig{
81
+ ListenNetwork: "tcp",
82
+ ListenAddress: fmt.Sprintf(
83
+ "%s:%d",
84
+ cfg.PrivateBindAddr,
85
+ cfg.PrivatePort,
86
+ ),
87
+ UseNtC: true,
88
+ },
89
+ )
90
+ }
91
+ if cfg.SocketPath != "" {
92
+ // Private UNIX socket (node-to-client)
93
+ listeners = append(
94
+ listeners,
95
+ dingo.ListenerConfig{
96
+ ListenNetwork: "unix",
97
+ ListenAddress: cfg.SocketPath,
98
+ UseNtC: true,
99
+ },
100
+ )
101
+ }
102
+ d, err := dingo.New(
103
+ dingo.NewConfig(
104
+ dingo.WithIntersectTip(cfg.IntersectTip),
105
+ dingo.WithLogger(logger),
106
+ dingo.WithDatabasePath(cfg.DatabasePath),
107
+ dingo.WithNetwork(cfg.Network),
108
+ dingo.WithCardanoNodeConfig(nodeCfg),
109
+ dingo.WithListeners(listeners...),
110
+ dingo.WithOutboundSourcePort(cfg.RelayPort),
111
+ dingo.WithUtxorpcPort(cfg.UtxorpcPort),
112
+ dingo.WithUtxorpcTlsCertFilePath(cfg.TlsCertFilePath),
113
+ dingo.WithUtxorpcTlsKeyFilePath(cfg.TlsKeyFilePath),
114
+ // Enable metrics with default prometheus registry
115
+ dingo.WithPrometheusRegistry(prometheus.DefaultRegisterer),
116
+ // TODO: make this configurable (#387)
117
+ // dingo.WithTracing(true),
118
+ dingo.WithTopologyConfig(config.GetTopologyConfig()),
119
+ ),
120
+ )
121
+ if err != nil {
122
+ return err
123
+ }
124
+ // Metrics and debug listener
125
+ http.Handle("/metrics", promhttp.Handler())
126
+ logger.Info(
127
+ "serving prometheus metrics on "+fmt.Sprintf(
128
+ "%s:%d",
129
+ cfg.BindAddr,
130
+ cfg.MetricsPort,
131
+ ),
132
+ "component",
133
+ "node",
134
+ )
135
+ go func() {
136
+ debugger := &http.Server{
137
+ Addr: fmt.Sprintf(
138
+ "%s:%d",
139
+ cfg.BindAddr,
140
+ cfg.MetricsPort,
141
+ ),
142
+ ReadHeaderTimeout: 60 * time.Second,
143
+ }
144
+ if err := debugger.ListenAndServe(); err != nil {
145
+ logger.Error(
146
+ fmt.Sprintf("failed to start metrics listener: %s", err),
147
+ "component", "node",
148
+ )
149
+ os.Exit(1)
150
+ }
151
+ }()
152
+ // Wait for interrupt/termination signal
153
+ signalCtx, signalCtxStop := signal.NotifyContext(
154
+ context.Background(),
155
+ syscall.SIGINT,
156
+ syscall.SIGTERM,
157
+ )
158
+ defer signalCtxStop()
159
+ go func() {
160
+ <-signalCtx.Done()
161
+ logger.Info("signal received, shutting down")
162
+ if err := d.Stop(); err != nil { // nolint:contextcheck
163
+ logger.Error(
164
+ "failure(s) while shutting down",
165
+ "error",
166
+ err,
167
+ )
168
+ }
169
+ os.Exit(0)
170
+ }()
171
+ // Run node
172
+ if err := d.Run(); err != nil {
173
+ return err
174
+ }
175
+ return nil
176
+ }
@@ -0,0 +1,33 @@
1
+ // Copyright 2024 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 version
16
+
17
+ import (
18
+ "fmt"
19
+ )
20
+
21
+ // These are populated at build time
22
+ var (
23
+ Version string
24
+ CommitHash string
25
+ )
26
+
27
+ func GetVersionString() string {
28
+ if Version != "" {
29
+ return fmt.Sprintf("%s (commit %s)", Version, CommitHash)
30
+ } else {
31
+ return fmt.Sprintf("devel (commit %s)", CommitHash)
32
+ }
33
+ }