@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.
- package/.dockerignore +5 -0
- package/.github/CODEOWNERS +5 -0
- package/.github/assets/dingo-ate-my-blockchain.png +0 -0
- package/.github/assets/dingo-illustration.png +0 -0
- package/.github/assets/dingo-logo-with-text-horizontal.png +0 -0
- package/.github/assets/dingo-logo-with-text.png +0 -0
- package/.github/dependabot.yml +19 -0
- package/.github/dingo-20241210.png +0 -0
- package/.github/dingo.md +56 -0
- package/.github/workflows/ci-docker.yml +36 -0
- package/.github/workflows/conventional-commits.yml +17 -0
- package/.github/workflows/go-test.yml +29 -0
- package/.github/workflows/golangci-lint.yml +23 -0
- package/.github/workflows/publish.yml +207 -0
- package/.golangci.yml +71 -0
- package/Dockerfile +25 -0
- package/LICENSE +201 -0
- package/Makefile +53 -0
- package/README.md +150 -0
- package/blockfetch.go +144 -0
- package/chain/chain.go +504 -0
- package/chain/chain_test.go +468 -0
- package/chain/errors.go +80 -0
- package/chain/event.go +33 -0
- package/chain/iter.go +64 -0
- package/chainsync/chainsync.go +97 -0
- package/chainsync.go +223 -0
- package/cmd/dingo/load.go +52 -0
- package/cmd/dingo/main.go +118 -0
- package/cmd/dingo/serve.go +49 -0
- package/config/cardano/node.go +192 -0
- package/config/cardano/node_test.go +85 -0
- package/config/cardano/preview/README.md +4 -0
- package/config/cardano/preview/alonzo-genesis.json +196 -0
- package/config/cardano/preview/byron-genesis.json +117 -0
- package/config/cardano/preview/config.json +114 -0
- package/config/cardano/preview/conway-genesis.json +297 -0
- package/config/cardano/preview/shelley-genesis.json +68 -0
- package/config.go +245 -0
- package/connmanager/connection_manager.go +105 -0
- package/connmanager/connection_manager_test.go +185 -0
- package/connmanager/event.go +37 -0
- package/connmanager/listener.go +140 -0
- package/connmanager/outbound.go +93 -0
- package/connmanager/socket.go +55 -0
- package/connmanager/unix.go +78 -0
- package/custom-p2p-topology.json +24 -0
- package/custom-p2p-topology.json.backup +24 -0
- package/custom-p2p-topology.json.mainnet +37 -0
- package/database/account.go +138 -0
- package/database/block.go +362 -0
- package/database/certs.go +53 -0
- package/database/commit_timestamp.go +77 -0
- package/database/database.go +118 -0
- package/database/database_test.go +62 -0
- package/database/drep.go +27 -0
- package/database/epoch.go +121 -0
- package/database/immutable/chunk.go +182 -0
- package/database/immutable/immutable.go +350 -0
- package/database/immutable/immutable_test.go +59 -0
- package/database/immutable/primary.go +106 -0
- package/database/immutable/secondary.go +103 -0
- package/database/immutable/testdata/08893.chunk +0 -0
- package/database/immutable/testdata/08893.primary +0 -0
- package/database/immutable/testdata/08893.secondary +0 -0
- package/database/immutable/testdata/08894.chunk +0 -0
- package/database/immutable/testdata/08894.primary +0 -0
- package/database/immutable/testdata/08894.secondary +0 -0
- package/database/immutable/testdata/README.md +4 -0
- package/database/plugin/blob/badger/commit_timestamp.go +50 -0
- package/database/plugin/blob/badger/database.go +152 -0
- package/database/plugin/blob/badger/logger.go +63 -0
- package/database/plugin/blob/badger/metrics.go +98 -0
- package/database/plugin/blob/blob.go +19 -0
- package/database/plugin/blob/store.go +40 -0
- package/database/plugin/log.go +27 -0
- package/database/plugin/metadata/metadata.go +19 -0
- package/database/plugin/metadata/sqlite/account.go +224 -0
- package/database/plugin/metadata/sqlite/certs.go +58 -0
- package/database/plugin/metadata/sqlite/commit_timestamp.go +68 -0
- package/database/plugin/metadata/sqlite/database.go +218 -0
- package/database/plugin/metadata/sqlite/epoch.go +120 -0
- package/database/plugin/metadata/sqlite/models/account.go +81 -0
- package/database/plugin/metadata/sqlite/models/auth_committee_hot.go +26 -0
- package/database/plugin/metadata/sqlite/models/deregistration_drep.go +26 -0
- package/database/plugin/metadata/sqlite/models/drep.go +27 -0
- package/database/plugin/metadata/sqlite/models/epoch.go +31 -0
- package/database/plugin/metadata/sqlite/models/models.go +45 -0
- package/database/plugin/metadata/sqlite/models/pool.go +97 -0
- package/database/plugin/metadata/sqlite/models/pparam_update.go +27 -0
- package/database/plugin/metadata/sqlite/models/pparams.go +27 -0
- package/database/plugin/metadata/sqlite/models/registration_drep.go +28 -0
- package/database/plugin/metadata/sqlite/models/resign_committee_cold.go +27 -0
- package/database/plugin/metadata/sqlite/models/stake_registration_delegation.go +27 -0
- package/database/plugin/metadata/sqlite/models/stake_vote_delegation.go +27 -0
- package/database/plugin/metadata/sqlite/models/stake_vote_registration_delegation.go +27 -0
- package/database/plugin/metadata/sqlite/models/tip.go +26 -0
- package/database/plugin/metadata/sqlite/models/update_drep.go +27 -0
- package/database/plugin/metadata/sqlite/models/utxo.go +30 -0
- package/database/plugin/metadata/sqlite/models/vote_delegation.go +26 -0
- package/database/plugin/metadata/sqlite/models/vote_registration_delegation.go +26 -0
- package/database/plugin/metadata/sqlite/pool.go +240 -0
- package/database/plugin/metadata/sqlite/pparams.go +110 -0
- package/database/plugin/metadata/sqlite/tip.go +83 -0
- package/database/plugin/metadata/sqlite/utxo.go +292 -0
- package/database/plugin/metadata/store.go +168 -0
- package/database/plugin/option.go +190 -0
- package/database/plugin/plugin.go +20 -0
- package/database/plugin/register.go +118 -0
- package/database/pparams.go +145 -0
- package/database/tip.go +45 -0
- package/database/txn.go +147 -0
- package/database/types/types.go +74 -0
- package/database/types/types_test.go +83 -0
- package/database/utxo.go +263 -0
- package/dist/artifacts.json +1 -0
- package/dist/checksums.txt +22 -0
- package/dist/config.yaml +253 -0
- package/dist/dingo-0.5.0-SNAPSHOT-d9431e4.tar.gz +0 -0
- package/dist/dingo-0.5.0-SNAPSHOT-d9431e4.tar.gz.sbom.json +1 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_darwin_arm64.tar.gz +0 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_darwin_arm64.tar.gz.sbom.json +1 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_darwin_x86_64.tar.gz +0 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_darwin_x86_64.tar.gz.sbom.json +1 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.apk +0 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.apk.sbom.json +1 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.deb +0 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.deb.sbom.json +1 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.rpm +0 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_amd64.rpm.sbom.json +1 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.apk +0 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.apk.sbom.json +1 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.deb +0 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.deb.sbom.json +1 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.rpm +0 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.rpm.sbom.json +1 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.tar.gz +0 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_arm64.tar.gz.sbom.json +1 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_x86_64.tar.gz +0 -0
- package/dist/dingo_0.5.0-SNAPSHOT-d9431e4_linux_x86_64.tar.gz.sbom.json +1 -0
- package/dist/dingo_darwin_amd64_v1/dingo +0 -0
- package/dist/dingo_darwin_arm64_v8.0/dingo +0 -0
- package/dist/dingo_linux_amd64_v1/dingo +0 -0
- package/dist/dingo_linux_arm64_v8.0/dingo +0 -0
- package/dist/homebrew/dingo.rb +51 -0
- package/dist/metadata.json +1 -0
- package/event/event.go +141 -0
- package/event/event_test.go +115 -0
- package/event/metrics.go +44 -0
- package/go.mod +98 -0
- package/go.sum +358 -0
- package/internal/config/config.go +145 -0
- package/internal/config/config_test.go +118 -0
- package/internal/node/load.go +149 -0
- package/internal/node/node.go +176 -0
- package/internal/version/version.go +33 -0
- package/ledger/certs.go +113 -0
- package/ledger/chainsync.go +578 -0
- package/ledger/eras/allegra.go +154 -0
- package/ledger/eras/alonzo.go +156 -0
- package/ledger/eras/babbage.go +154 -0
- package/ledger/eras/byron.go +42 -0
- package/ledger/eras/conway.go +158 -0
- package/ledger/eras/eras.go +44 -0
- package/ledger/eras/mary.go +154 -0
- package/ledger/eras/shelley.go +164 -0
- package/ledger/error.go +19 -0
- package/ledger/event.go +50 -0
- package/ledger/metrics.go +53 -0
- package/ledger/queries.go +260 -0
- package/ledger/slot.go +127 -0
- package/ledger/slot_test.go +147 -0
- package/ledger/state.go +726 -0
- package/ledger/view.go +73 -0
- package/localstatequery.go +50 -0
- package/localtxmonitor.go +44 -0
- package/localtxsubmission.go +52 -0
- package/mempool/consumer.go +98 -0
- package/mempool/mempool.go +322 -0
- package/node.go +320 -0
- package/package.json +33 -0
- package/peergov/event.go +27 -0
- package/peergov/peer.go +67 -0
- package/peergov/peergov.go +290 -0
- package/peersharing.go +70 -0
- package/preview-local-topology.json +23 -0
- package/topology/topology.go +69 -0
- package/topology/topology_test.go +179 -0
- package/tracing.go +65 -0
- package/txsubmission.go +233 -0
- package/utxorpc/query.go +311 -0
- package/utxorpc/submit.go +395 -0
- package/utxorpc/sync.go +276 -0
- package/utxorpc/utxorpc.go +166 -0
- package/utxorpc/watch.go +310 -0
|
@@ -0,0 +1,350 @@
|
|
|
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 immutable
|
|
16
|
+
|
|
17
|
+
import (
|
|
18
|
+
"fmt"
|
|
19
|
+
"os"
|
|
20
|
+
"path/filepath"
|
|
21
|
+
"slices"
|
|
22
|
+
"strings"
|
|
23
|
+
|
|
24
|
+
ocommon "github.com/blinklabs-io/gouroboros/protocol/common"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
type ImmutableDb struct {
|
|
28
|
+
dataDir string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type Block struct {
|
|
32
|
+
Type uint
|
|
33
|
+
Slot uint64
|
|
34
|
+
Hash []byte
|
|
35
|
+
IsEbb bool
|
|
36
|
+
Cbor []byte
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// New returns a new ImmutableDb using the specified data directory or an error
|
|
40
|
+
func New(dataDir string) (*ImmutableDb, error) {
|
|
41
|
+
if _, err := os.Stat(dataDir); err != nil {
|
|
42
|
+
return nil, err
|
|
43
|
+
}
|
|
44
|
+
i := &ImmutableDb{
|
|
45
|
+
dataDir: dataDir,
|
|
46
|
+
}
|
|
47
|
+
return i, nil
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func (i *ImmutableDb) getChunkNames() ([]string, error) {
|
|
51
|
+
ret := []string{}
|
|
52
|
+
files, err := os.ReadDir(i.dataDir)
|
|
53
|
+
if err != nil {
|
|
54
|
+
return nil, err
|
|
55
|
+
}
|
|
56
|
+
for _, entry := range files {
|
|
57
|
+
entryName := entry.Name()
|
|
58
|
+
entryExt := filepath.Ext(entryName)
|
|
59
|
+
if entryExt != chunkFileExtension {
|
|
60
|
+
continue
|
|
61
|
+
}
|
|
62
|
+
chunkName := strings.TrimSuffix(entryName, entryExt)
|
|
63
|
+
ret = append(ret, chunkName)
|
|
64
|
+
}
|
|
65
|
+
slices.Sort(ret)
|
|
66
|
+
return ret, nil
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
func (i *ImmutableDb) getChunkNamesFromPoint(
|
|
70
|
+
point ocommon.Point,
|
|
71
|
+
) ([]string, error) {
|
|
72
|
+
chunkNames, err := i.getChunkNames()
|
|
73
|
+
if err != nil {
|
|
74
|
+
return nil, err
|
|
75
|
+
}
|
|
76
|
+
// Return all chunks for the origin
|
|
77
|
+
if point.Slot == 0 {
|
|
78
|
+
return chunkNames, nil
|
|
79
|
+
}
|
|
80
|
+
lowerBound := 0
|
|
81
|
+
upperBound := len(chunkNames)
|
|
82
|
+
for lowerBound <= upperBound {
|
|
83
|
+
// Get chunk in the middle of the current bounds
|
|
84
|
+
middlePoint := (lowerBound + upperBound) / 2
|
|
85
|
+
middleChunkName := chunkNames[middlePoint]
|
|
86
|
+
middleSecondary, err := i.getChunkSecondaryIndex(middleChunkName)
|
|
87
|
+
if err != nil {
|
|
88
|
+
return nil, err
|
|
89
|
+
}
|
|
90
|
+
next, err := middleSecondary.Next()
|
|
91
|
+
if err != nil {
|
|
92
|
+
return nil, err
|
|
93
|
+
}
|
|
94
|
+
if next == nil {
|
|
95
|
+
break
|
|
96
|
+
}
|
|
97
|
+
startSlot := next.BlockOrEbb
|
|
98
|
+
var endSlot uint64
|
|
99
|
+
for {
|
|
100
|
+
next, err := middleSecondary.Next()
|
|
101
|
+
if err != nil {
|
|
102
|
+
return nil, err
|
|
103
|
+
}
|
|
104
|
+
if next == nil {
|
|
105
|
+
break
|
|
106
|
+
}
|
|
107
|
+
endSlot = next.BlockOrEbb
|
|
108
|
+
}
|
|
109
|
+
if point.Slot < startSlot {
|
|
110
|
+
// The slot we're looking for is less than the first slot in the chunk, so
|
|
111
|
+
// we can eliminate all later chunks
|
|
112
|
+
upperBound = middlePoint - 1
|
|
113
|
+
} else if point.Slot > endSlot {
|
|
114
|
+
// The slot we're looking for is greater than the last slot in the chunk, so
|
|
115
|
+
// we can eliminate all earlier chunks
|
|
116
|
+
lowerBound = middlePoint + 1
|
|
117
|
+
} else {
|
|
118
|
+
// We found the chunk that (probably) has the requested point
|
|
119
|
+
break
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return chunkNames[lowerBound:], nil
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
func (i *ImmutableDb) getChunkPrimaryIndex(
|
|
126
|
+
chunkName string,
|
|
127
|
+
) (*primaryIndex, error) {
|
|
128
|
+
primaryFilePath := filepath.Join(
|
|
129
|
+
i.dataDir,
|
|
130
|
+
chunkName+primaryFileExtension,
|
|
131
|
+
)
|
|
132
|
+
primary := newPrimaryIndex()
|
|
133
|
+
if err := primary.Open(primaryFilePath); err != nil {
|
|
134
|
+
return nil, fmt.Errorf(
|
|
135
|
+
"failed to read primary index: %s: %w",
|
|
136
|
+
primaryFilePath,
|
|
137
|
+
err,
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
return primary, nil
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
func (i *ImmutableDb) getChunkSecondaryIndex(
|
|
144
|
+
chunkName string,
|
|
145
|
+
) (*secondaryIndex, error) {
|
|
146
|
+
primary, err := i.getChunkPrimaryIndex(chunkName)
|
|
147
|
+
if err != nil {
|
|
148
|
+
return nil, err
|
|
149
|
+
}
|
|
150
|
+
secondaryFilePath := filepath.Join(
|
|
151
|
+
i.dataDir,
|
|
152
|
+
chunkName+secondaryFileExtension,
|
|
153
|
+
)
|
|
154
|
+
secondary := newSecondaryIndex()
|
|
155
|
+
if err := secondary.Open(secondaryFilePath, primary); err != nil {
|
|
156
|
+
return nil, fmt.Errorf(
|
|
157
|
+
"failed to read secondary index: %s: %w",
|
|
158
|
+
secondaryFilePath,
|
|
159
|
+
err,
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
return secondary, nil
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
func (i *ImmutableDb) getChunk(chunkName string) (*chunk, error) {
|
|
166
|
+
// Open secondary index
|
|
167
|
+
secondary, err := i.getChunkSecondaryIndex(chunkName)
|
|
168
|
+
if err != nil {
|
|
169
|
+
return nil, err
|
|
170
|
+
}
|
|
171
|
+
// Open chunk
|
|
172
|
+
chunkFilePath := filepath.Join(
|
|
173
|
+
i.dataDir,
|
|
174
|
+
chunkName+chunkFileExtension,
|
|
175
|
+
)
|
|
176
|
+
chunk := newChunk()
|
|
177
|
+
if err := chunk.Open(chunkFilePath, secondary); err != nil {
|
|
178
|
+
return nil, fmt.Errorf(
|
|
179
|
+
"failed to read chunk: %s: %w",
|
|
180
|
+
chunkFilePath,
|
|
181
|
+
err,
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
return chunk, nil
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
func (i *ImmutableDb) GetTip() (*ocommon.Point, error) {
|
|
188
|
+
var ret *ocommon.Point
|
|
189
|
+
chunkNames, err := i.getChunkNames()
|
|
190
|
+
if err != nil {
|
|
191
|
+
return nil, err
|
|
192
|
+
}
|
|
193
|
+
if len(chunkNames) == 0 {
|
|
194
|
+
return nil, nil
|
|
195
|
+
}
|
|
196
|
+
secondary, err := i.getChunkSecondaryIndex(chunkNames[len(chunkNames)-1])
|
|
197
|
+
if err != nil {
|
|
198
|
+
return nil, err
|
|
199
|
+
}
|
|
200
|
+
var tmpPoint ocommon.Point
|
|
201
|
+
for {
|
|
202
|
+
next, err := secondary.Next()
|
|
203
|
+
if err != nil {
|
|
204
|
+
return nil, err
|
|
205
|
+
}
|
|
206
|
+
if next == nil {
|
|
207
|
+
break
|
|
208
|
+
}
|
|
209
|
+
tmpPoint = ocommon.NewPoint(
|
|
210
|
+
next.BlockOrEbb,
|
|
211
|
+
next.HeaderHash[:],
|
|
212
|
+
)
|
|
213
|
+
ret = &tmpPoint
|
|
214
|
+
}
|
|
215
|
+
return ret, nil
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
func (i *ImmutableDb) GetBlock(point ocommon.Point) (*Block, error) {
|
|
219
|
+
var err error
|
|
220
|
+
chunkNames, err := i.getChunkNamesFromPoint(point)
|
|
221
|
+
if err != nil {
|
|
222
|
+
return nil, err
|
|
223
|
+
}
|
|
224
|
+
chunk, err := i.getChunk(chunkNames[0])
|
|
225
|
+
if err != nil {
|
|
226
|
+
return nil, err
|
|
227
|
+
}
|
|
228
|
+
var tmpBlock *Block
|
|
229
|
+
for {
|
|
230
|
+
tmpBlock, err = chunk.Next()
|
|
231
|
+
if err != nil {
|
|
232
|
+
return nil, err
|
|
233
|
+
}
|
|
234
|
+
if tmpBlock == nil {
|
|
235
|
+
break
|
|
236
|
+
}
|
|
237
|
+
if tmpBlock.Slot != point.Slot {
|
|
238
|
+
continue
|
|
239
|
+
}
|
|
240
|
+
if string(tmpBlock.Hash) != string(point.Hash) {
|
|
241
|
+
continue
|
|
242
|
+
}
|
|
243
|
+
return tmpBlock, nil
|
|
244
|
+
}
|
|
245
|
+
return nil, nil
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
func (i *ImmutableDb) TruncateChunksFromPoint(point ocommon.Point) error {
|
|
249
|
+
chunkNames, err := i.getChunkNamesFromPoint(point)
|
|
250
|
+
if err != nil {
|
|
251
|
+
return err
|
|
252
|
+
}
|
|
253
|
+
for _, chunkName := range chunkNames {
|
|
254
|
+
chunkPathPrefix := filepath.Join(
|
|
255
|
+
i.dataDir,
|
|
256
|
+
chunkName,
|
|
257
|
+
)
|
|
258
|
+
if err := os.Remove(chunkPathPrefix + chunkFileExtension); err != nil {
|
|
259
|
+
return err
|
|
260
|
+
}
|
|
261
|
+
if err := os.Remove(chunkPathPrefix + secondaryFileExtension); err != nil {
|
|
262
|
+
return err
|
|
263
|
+
}
|
|
264
|
+
if err := os.Remove(chunkPathPrefix + primaryFileExtension); err != nil {
|
|
265
|
+
return err
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return nil
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
func (i *ImmutableDb) BlocksFromPoint(
|
|
272
|
+
point ocommon.Point,
|
|
273
|
+
) (*BlockIterator, error) {
|
|
274
|
+
chunkNames, err := i.getChunkNamesFromPoint(point)
|
|
275
|
+
if err != nil {
|
|
276
|
+
return nil, err
|
|
277
|
+
}
|
|
278
|
+
ret := &BlockIterator{
|
|
279
|
+
db: i,
|
|
280
|
+
chunkNames: chunkNames[:],
|
|
281
|
+
startPoint: point,
|
|
282
|
+
}
|
|
283
|
+
return ret, nil
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
type BlockIterator struct {
|
|
287
|
+
db *ImmutableDb
|
|
288
|
+
startPoint ocommon.Point
|
|
289
|
+
foundStartPoint bool
|
|
290
|
+
chunkNames []string
|
|
291
|
+
chunkIdx int
|
|
292
|
+
chunk *chunk
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
func (b *BlockIterator) Next() (*Block, error) {
|
|
296
|
+
var err error
|
|
297
|
+
var tmpChunk *chunk
|
|
298
|
+
if b.chunk == nil {
|
|
299
|
+
if b.chunkIdx == 0 && len(b.chunkNames) > 0 {
|
|
300
|
+
// Open initial chunk
|
|
301
|
+
tmpChunk, err = b.db.getChunk(b.chunkNames[b.chunkIdx])
|
|
302
|
+
if err != nil {
|
|
303
|
+
return nil, err
|
|
304
|
+
}
|
|
305
|
+
b.chunk = tmpChunk
|
|
306
|
+
} else {
|
|
307
|
+
return nil, nil
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
var tmpBlock *Block
|
|
311
|
+
for {
|
|
312
|
+
tmpBlock, err = b.chunk.Next()
|
|
313
|
+
if err != nil {
|
|
314
|
+
return nil, err
|
|
315
|
+
}
|
|
316
|
+
if tmpBlock == nil {
|
|
317
|
+
// We've reached the end of the current chunk
|
|
318
|
+
if err := b.chunk.Close(); err != nil {
|
|
319
|
+
return nil, err
|
|
320
|
+
}
|
|
321
|
+
b.chunk = nil
|
|
322
|
+
b.chunkIdx++
|
|
323
|
+
if b.chunkIdx >= len(b.chunkNames) {
|
|
324
|
+
return nil, nil
|
|
325
|
+
}
|
|
326
|
+
tmpChunk, err = b.db.getChunk(b.chunkNames[b.chunkIdx])
|
|
327
|
+
if err != nil {
|
|
328
|
+
return nil, err
|
|
329
|
+
}
|
|
330
|
+
b.chunk = tmpChunk
|
|
331
|
+
continue
|
|
332
|
+
}
|
|
333
|
+
if !b.foundStartPoint {
|
|
334
|
+
if tmpBlock.Slot < b.startPoint.Slot {
|
|
335
|
+
continue
|
|
336
|
+
}
|
|
337
|
+
b.foundStartPoint = true
|
|
338
|
+
}
|
|
339
|
+
return tmpBlock, nil
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
func (b *BlockIterator) Close() error {
|
|
344
|
+
if b.chunk != nil {
|
|
345
|
+
if err := b.chunk.Close(); err != nil {
|
|
346
|
+
return err
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return nil
|
|
350
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
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 immutable_test
|
|
16
|
+
|
|
17
|
+
import (
|
|
18
|
+
"encoding/hex"
|
|
19
|
+
"testing"
|
|
20
|
+
|
|
21
|
+
"github.com/blinklabs-io/dingo/database/immutable"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
const (
|
|
25
|
+
testDataDir = "./testdata"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
func TestGetTip(t *testing.T) {
|
|
29
|
+
// These expected values correspond to the last block in our test data
|
|
30
|
+
var expectedSlot uint64 = 38426380
|
|
31
|
+
expectedHash := "7ada6ed78f6caa499370da6548b143c59320f5e5283e5e80e202a994ba7bfebf"
|
|
32
|
+
imm, err := immutable.New(testDataDir)
|
|
33
|
+
if err != nil {
|
|
34
|
+
t.Fatalf("unexpected error: %s", err)
|
|
35
|
+
}
|
|
36
|
+
tip, err := imm.GetTip()
|
|
37
|
+
if err != nil {
|
|
38
|
+
t.Fatalf("unexpected error: %s", err)
|
|
39
|
+
}
|
|
40
|
+
if tip == nil {
|
|
41
|
+
t.Fatalf("did not get expected tip value, got nil instead")
|
|
42
|
+
}
|
|
43
|
+
if tip.Slot != expectedSlot {
|
|
44
|
+
t.Fatalf(
|
|
45
|
+
"did not get expected slot value: expected %d, got %d",
|
|
46
|
+
expectedSlot,
|
|
47
|
+
tip.Slot,
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
if hex.EncodeToString(tip.Hash) != expectedHash {
|
|
51
|
+
t.Fatalf(
|
|
52
|
+
"did not get expected hash value: expected %s, got %x",
|
|
53
|
+
expectedHash,
|
|
54
|
+
tip.Hash,
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// TODO: add tests for getting a specific block and getting a range of blocks that traverses multiple chunks (#386)
|
|
@@ -0,0 +1,106 @@
|
|
|
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 immutable
|
|
16
|
+
|
|
17
|
+
import (
|
|
18
|
+
"encoding/binary"
|
|
19
|
+
"errors"
|
|
20
|
+
"io"
|
|
21
|
+
"os"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
const (
|
|
25
|
+
primaryFileExtension = ".primary"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
type primaryIndex struct {
|
|
29
|
+
file *os.File
|
|
30
|
+
slot int
|
|
31
|
+
lastOffset uint32
|
|
32
|
+
version uint8
|
|
33
|
+
seenFirstOffset bool
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type primaryIndexEntry struct {
|
|
37
|
+
RelativeSlot int
|
|
38
|
+
SecondaryOffset uint32
|
|
39
|
+
empty bool
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
func newPrimaryIndex() *primaryIndex {
|
|
43
|
+
return &primaryIndex{}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
func (e primaryIndexEntry) Empty() bool {
|
|
47
|
+
return e.empty
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func (p *primaryIndex) Open(path string) error {
|
|
51
|
+
f, err := os.Open(path)
|
|
52
|
+
if err != nil {
|
|
53
|
+
return err
|
|
54
|
+
}
|
|
55
|
+
p.file = f
|
|
56
|
+
// Read version
|
|
57
|
+
if err := binary.Read(f, binary.BigEndian, &p.version); err != nil {
|
|
58
|
+
return err
|
|
59
|
+
}
|
|
60
|
+
return nil
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
func (p *primaryIndex) Close() error {
|
|
64
|
+
return p.file.Close()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
func (p *primaryIndex) Next() (*primaryIndexEntry, error) {
|
|
68
|
+
var tmpOffset uint32
|
|
69
|
+
if err := binary.Read(p.file, binary.BigEndian, &tmpOffset); err != nil {
|
|
70
|
+
if errors.Is(err, io.EOF) {
|
|
71
|
+
// We've reached the end of the file
|
|
72
|
+
return nil, nil
|
|
73
|
+
}
|
|
74
|
+
return nil, err
|
|
75
|
+
}
|
|
76
|
+
empty := true
|
|
77
|
+
if tmpOffset > p.lastOffset || !p.seenFirstOffset {
|
|
78
|
+
empty = false
|
|
79
|
+
p.lastOffset = tmpOffset
|
|
80
|
+
}
|
|
81
|
+
tmpEntry := primaryIndexEntry{
|
|
82
|
+
RelativeSlot: p.slot,
|
|
83
|
+
SecondaryOffset: tmpOffset,
|
|
84
|
+
empty: empty,
|
|
85
|
+
}
|
|
86
|
+
p.slot++
|
|
87
|
+
p.seenFirstOffset = true
|
|
88
|
+
return &tmpEntry, nil
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
func (p *primaryIndex) NextOccupied() (*primaryIndexEntry, error) {
|
|
92
|
+
for {
|
|
93
|
+
next, err := p.Next()
|
|
94
|
+
if err != nil {
|
|
95
|
+
return nil, err
|
|
96
|
+
}
|
|
97
|
+
if next == nil {
|
|
98
|
+
break
|
|
99
|
+
}
|
|
100
|
+
if next.Empty() {
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
103
|
+
return next, nil
|
|
104
|
+
}
|
|
105
|
+
return nil, nil
|
|
106
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
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 immutable
|
|
16
|
+
|
|
17
|
+
import (
|
|
18
|
+
"encoding/binary"
|
|
19
|
+
"fmt"
|
|
20
|
+
"os"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
const (
|
|
24
|
+
secondaryFileExtension = ".secondary"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
type secondaryIndex struct {
|
|
28
|
+
file *os.File
|
|
29
|
+
fileSize int64
|
|
30
|
+
primary *primaryIndex
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type secondaryIndexEntryInner struct {
|
|
34
|
+
BlockOffset uint64
|
|
35
|
+
HeaderOffset uint16
|
|
36
|
+
HeaderSize uint16
|
|
37
|
+
Checksum uint32
|
|
38
|
+
HeaderHash [32]byte
|
|
39
|
+
BlockOrEbb uint64
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type secondaryIndexEntry struct {
|
|
43
|
+
secondaryIndexEntryInner
|
|
44
|
+
IsEbb bool
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func newSecondaryIndex() *secondaryIndex {
|
|
48
|
+
return &secondaryIndex{}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
func (s *secondaryIndex) Open(path string, primary *primaryIndex) error {
|
|
52
|
+
f, err := os.Open(path)
|
|
53
|
+
if err != nil {
|
|
54
|
+
return err
|
|
55
|
+
}
|
|
56
|
+
s.file = f
|
|
57
|
+
s.primary = primary
|
|
58
|
+
if stat, err := f.Stat(); err != nil {
|
|
59
|
+
return err
|
|
60
|
+
} else {
|
|
61
|
+
s.fileSize = stat.Size()
|
|
62
|
+
}
|
|
63
|
+
return nil
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
func (s *secondaryIndex) Close() error {
|
|
67
|
+
if err := s.primary.Close(); err != nil {
|
|
68
|
+
return err
|
|
69
|
+
}
|
|
70
|
+
return s.file.Close()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
func (s *secondaryIndex) Next() (*secondaryIndexEntry, error) {
|
|
74
|
+
nextOccupied, err := s.primary.NextOccupied()
|
|
75
|
+
if err != nil {
|
|
76
|
+
return nil, err
|
|
77
|
+
}
|
|
78
|
+
if nextOccupied == nil {
|
|
79
|
+
return nil, nil
|
|
80
|
+
}
|
|
81
|
+
// Look for final offset
|
|
82
|
+
if int64(nextOccupied.SecondaryOffset) == s.fileSize {
|
|
83
|
+
return nil, nil
|
|
84
|
+
}
|
|
85
|
+
// Seek to offset
|
|
86
|
+
if _, err := s.file.Seek(int64(nextOccupied.SecondaryOffset), 0); err != nil {
|
|
87
|
+
return nil, fmt.Errorf("failed while seeking: %w", err)
|
|
88
|
+
}
|
|
89
|
+
// Read entry
|
|
90
|
+
var tmpEntryInner secondaryIndexEntryInner
|
|
91
|
+
if err := binary.Read(s.file, binary.BigEndian, &tmpEntryInner); err != nil {
|
|
92
|
+
return nil, fmt.Errorf("failed while reading: %w", err)
|
|
93
|
+
}
|
|
94
|
+
ret := &secondaryIndexEntry{
|
|
95
|
+
secondaryIndexEntryInner: tmpEntryInner,
|
|
96
|
+
}
|
|
97
|
+
// Check for EBB
|
|
98
|
+
// A block with its secondary index in the first slot with a small BlockOrEbb value is *probably* an EBB
|
|
99
|
+
if nextOccupied.RelativeSlot == 0 && ret.BlockOrEbb < 200 {
|
|
100
|
+
ret.IsEbb = true
|
|
101
|
+
}
|
|
102
|
+
return ret, nil
|
|
103
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,50 @@
|
|
|
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 badger
|
|
16
|
+
|
|
17
|
+
import (
|
|
18
|
+
"math/big"
|
|
19
|
+
|
|
20
|
+
badger "github.com/dgraph-io/badger/v4"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
const (
|
|
24
|
+
commitTimestampBlobKey = "metadata_commit_timestamp"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
func (b *BlobStoreBadger) GetCommitTimestamp() (int64, error) {
|
|
28
|
+
txn := b.NewTransaction(false)
|
|
29
|
+
item, err := txn.Get([]byte(commitTimestampBlobKey))
|
|
30
|
+
if err != nil {
|
|
31
|
+
return 0, err
|
|
32
|
+
}
|
|
33
|
+
val, err := item.ValueCopy(nil)
|
|
34
|
+
if err != nil {
|
|
35
|
+
return 0, err
|
|
36
|
+
}
|
|
37
|
+
return new(big.Int).SetBytes(val).Int64(), nil
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
func (b *BlobStoreBadger) SetCommitTimestamp(
|
|
41
|
+
txn *badger.Txn,
|
|
42
|
+
timestamp int64,
|
|
43
|
+
) error {
|
|
44
|
+
// Update badger
|
|
45
|
+
tmpTimestamp := new(big.Int).SetInt64(timestamp)
|
|
46
|
+
if err := txn.Set([]byte(commitTimestampBlobKey), tmpTimestamp.Bytes()); err != nil {
|
|
47
|
+
return err
|
|
48
|
+
}
|
|
49
|
+
return nil
|
|
50
|
+
}
|