Skip to content

Commit 6bcf424

Browse files
Merge pull request #98 from coinbase/patrick/support-block-gaps
Support Omitted Blocks
2 parents 0edccc4 + e455c22 commit 6bcf424

File tree

5 files changed

+204
-70
lines changed

5 files changed

+204
-70
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/coinbase/rosetta-cli
33
go 1.13
44

55
require (
6-
github.com/coinbase/rosetta-sdk-go v0.3.4-0.20200807162047-31075a509b1f
6+
github.com/coinbase/rosetta-sdk-go v0.3.4
77
github.com/dgraph-io/badger/v2 v2.0.3
88
github.com/dgraph-io/ristretto v0.0.2 // indirect
99
github.com/ethereum/go-ethereum v1.9.18

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+
5454
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
5555
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
5656
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U=
57-
github.com/coinbase/rosetta-sdk-go v0.3.4-0.20200807162047-31075a509b1f h1:U69ZwbTR10diY1MDi9LP/RxetVJzjd4bvIDUEs8XYvk=
58-
github.com/coinbase/rosetta-sdk-go v0.3.4-0.20200807162047-31075a509b1f/go.mod h1:Q6dAY0kdG2X3jNaIYnkxnZOb8XEZQar9Q1RcnBgm/wQ=
57+
github.com/coinbase/rosetta-sdk-go v0.3.4 h1:jWKgajozco/T0FNnZb2TqBsmsUoF6ZuCLnUJkEE+vNg=
58+
github.com/coinbase/rosetta-sdk-go v0.3.4/go.mod h1:Q6dAY0kdG2X3jNaIYnkxnZOb8XEZQar9Q1RcnBgm/wQ=
5959
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
6060
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
6161
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=

pkg/processor/reconciler_helper.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (h *ReconcilerHelper) BlockExists(
4949
ctx context.Context,
5050
block *types.BlockIdentifier,
5151
) (bool, error) {
52-
_, err := h.blockStorage.GetBlock(ctx, block)
52+
_, err := h.blockStorage.GetBlock(ctx, types.ConstructPartialBlockIdentifier(block))
5353
if err == nil {
5454
return true, nil
5555
}

pkg/storage/block_storage.go

Lines changed: 101 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,8 @@ const (
3535
// blockNamespace is prepended to any stored block.
3636
blockNamespace = "block"
3737

38-
// blockHashNamespace is prepended to any stored block hash.
39-
// We cannot just use the stored block key to lookup whether
40-
// a hash has been used before because it is concatenated
41-
// with the index of the stored block.
42-
blockHashNamespace = "block-hash"
38+
// blockIndexNamespace is prepended to any stored block index.
39+
blockIndexNamespace = "block-index"
4340

4441
// transactionHashNamespace is prepended to any stored
4542
// transaction hash.
@@ -55,9 +52,9 @@ var (
5552
// found in BlockStorage.
5653
ErrBlockNotFound = errors.New("block not found")
5754

58-
// ErrDuplicateBlockHash is returned when a block hash
55+
// ErrDuplicateKey is returned when a key
5956
// cannot be stored because it is a duplicate.
60-
ErrDuplicateBlockHash = errors.New("duplicate block hash")
57+
ErrDuplicateKey = errors.New("duplicate key")
6158

6259
// ErrDuplicateTransactionHash is returned when a transaction
6360
// hash cannot be stored because it is a duplicate.
@@ -68,14 +65,12 @@ func getHeadBlockKey() []byte {
6865
return []byte(headBlockKey)
6966
}
7067

71-
func getBlockKey(blockIdentifier *types.BlockIdentifier) []byte {
72-
return []byte(
73-
fmt.Sprintf("%s/%s/%d", blockNamespace, blockIdentifier.Hash, blockIdentifier.Index),
74-
)
68+
func getBlockHashKey(hash string) []byte {
69+
return []byte(fmt.Sprintf("%s/%s", blockNamespace, hash))
7570
}
7671

77-
func getBlockHashKey(blockIdentifier *types.BlockIdentifier) []byte {
78-
return []byte(fmt.Sprintf("%s/%s", blockHashNamespace, blockIdentifier.Hash))
72+
func getBlockIndexKey(index int64) []byte {
73+
return []byte(fmt.Sprintf("%s/%d", blockIndexNamespace, index))
7974
}
8075

8176
func getTransactionHashKey(transactionIdentifier *types.TransactionIdentifier) []byte {
@@ -174,18 +169,42 @@ func (b *BlockStorage) StoreHeadBlockIdentifier(
174169
// GetBlock returns a block, if it exists.
175170
func (b *BlockStorage) GetBlock(
176171
ctx context.Context,
177-
blockIdentifier *types.BlockIdentifier,
172+
blockIdentifier *types.PartialBlockIdentifier,
178173
) (*types.Block, error) {
179174
transaction := b.db.NewDatabaseTransaction(ctx, false)
180175
defer transaction.Discard(ctx)
181176

182-
exists, block, err := transaction.Get(ctx, getBlockKey(blockIdentifier))
177+
var exists bool
178+
var block []byte
179+
var err error
180+
switch {
181+
case blockIdentifier == nil || (blockIdentifier.Hash == nil && blockIdentifier.Index == nil):
182+
// Get current block when no blockIdentifier is provided
183+
var head *types.BlockIdentifier
184+
head, err = b.GetHeadBlockIdentifierTransactional(ctx, transaction)
185+
if err != nil {
186+
return nil, fmt.Errorf("%w: cannot get head block identifier", err)
187+
}
188+
189+
exists, block, err = transaction.Get(ctx, getBlockHashKey(head.Hash))
190+
case blockIdentifier.Hash != nil:
191+
// Get block by hash if provided
192+
exists, block, err = transaction.Get(ctx, getBlockHashKey(*blockIdentifier.Hash))
193+
default:
194+
// Get block by index if hash not provided
195+
var blockKey []byte
196+
exists, blockKey, err = transaction.Get(ctx, getBlockIndexKey(*blockIdentifier.Index))
197+
if exists {
198+
exists, block, err = transaction.Get(ctx, blockKey)
199+
}
200+
}
201+
183202
if err != nil {
184-
return nil, err
203+
return nil, fmt.Errorf("%w: unable to get block", err)
185204
}
186205

187206
if !exists {
188-
return nil, fmt.Errorf("%w %+v", ErrBlockNotFound, blockIdentifier)
207+
return nil, fmt.Errorf("%w: %+v", ErrBlockNotFound, blockIdentifier)
189208
}
190209

191210
var rosettaBlock types.Block
@@ -197,33 +216,48 @@ func (b *BlockStorage) GetBlock(
197216
return &rosettaBlock, nil
198217
}
199218

200-
// AddBlock stores a block or returns an error.
201-
func (b *BlockStorage) AddBlock(
219+
func (b *BlockStorage) storeBlock(
202220
ctx context.Context,
221+
transaction DatabaseTransaction,
203222
block *types.Block,
204223
) error {
205-
transaction := b.db.NewDatabaseTransaction(ctx, true)
206-
defer transaction.Discard(ctx)
207-
208224
buf, err := encode(block)
209225
if err != nil {
210-
return err
226+
return fmt.Errorf("%w: unable to encode block", err)
211227
}
212228

213-
// Store block
214-
err = transaction.Set(ctx, getBlockKey(block.BlockIdentifier), buf)
215-
if err != nil {
216-
return err
229+
if err := b.storeUniqueKey(ctx, transaction, getBlockHashKey(block.BlockIdentifier.Hash), buf); err != nil {
230+
return fmt.Errorf("%w: unable to store block", err)
217231
}
218232

219-
if err = b.StoreHeadBlockIdentifier(ctx, transaction, block.BlockIdentifier); err != nil {
220-
return err
233+
if err := b.storeUniqueKey(
234+
ctx,
235+
transaction,
236+
getBlockIndexKey(block.BlockIdentifier.Index),
237+
getBlockHashKey(block.BlockIdentifier.Hash),
238+
); err != nil {
239+
return fmt.Errorf("%w: unable to store block index", err)
240+
}
241+
242+
if err := b.StoreHeadBlockIdentifier(ctx, transaction, block.BlockIdentifier); err != nil {
243+
return fmt.Errorf("%w: unable to update head block identifier", err)
221244
}
222245

223-
// Store block hash
224-
err = b.storeBlockHash(ctx, transaction, block.BlockIdentifier)
246+
return nil
247+
}
248+
249+
// AddBlock stores a block or returns an error.
250+
func (b *BlockStorage) AddBlock(
251+
ctx context.Context,
252+
block *types.Block,
253+
) error {
254+
transaction := b.db.NewDatabaseTransaction(ctx, true)
255+
defer transaction.Discard(ctx)
256+
257+
// Store block
258+
err := b.storeBlock(ctx, transaction, block)
225259
if err != nil {
226-
return fmt.Errorf("%w: unable to store block hash", err)
260+
return fmt.Errorf("%w: unable to store block", err)
227261
}
228262

229263
// Store all transaction hashes
@@ -242,6 +276,27 @@ func (b *BlockStorage) AddBlock(
242276
return b.callWorkersAndCommit(ctx, block, transaction, true)
243277
}
244278

279+
func (b *BlockStorage) deleteBlock(
280+
ctx context.Context,
281+
transaction DatabaseTransaction,
282+
block *types.Block,
283+
) error {
284+
blockIdentifier := block.BlockIdentifier
285+
if err := transaction.Delete(ctx, getBlockHashKey(blockIdentifier.Hash)); err != nil {
286+
return fmt.Errorf("%w: unable to delete block", err)
287+
}
288+
289+
if err := transaction.Delete(ctx, getBlockIndexKey(blockIdentifier.Index)); err != nil {
290+
return fmt.Errorf("%w: unable to delete block index", err)
291+
}
292+
293+
if err := b.StoreHeadBlockIdentifier(ctx, transaction, block.ParentBlockIdentifier); err != nil {
294+
return fmt.Errorf("%w: unable to update head block identifier", err)
295+
}
296+
297+
return nil
298+
}
299+
245300
// RemoveBlock removes a block or returns an error.
246301
// RemoveBlock also removes the block hash and all
247302
// its transaction hashes to not break duplicate
@@ -250,7 +305,7 @@ func (b *BlockStorage) RemoveBlock(
250305
ctx context.Context,
251306
blockIdentifier *types.BlockIdentifier,
252307
) error {
253-
block, err := b.GetBlock(ctx, blockIdentifier)
308+
block, err := b.GetBlock(ctx, types.ConstructPartialBlockIdentifier(blockIdentifier))
254309
if err != nil {
255310
return err
256311
}
@@ -266,19 +321,9 @@ func (b *BlockStorage) RemoveBlock(
266321
}
267322
}
268323

269-
// Remove block hash
270-
err = transaction.Delete(ctx, getBlockHashKey(blockIdentifier))
271-
if err != nil {
272-
return err
273-
}
274-
275-
// Remove block
276-
if err := transaction.Delete(ctx, getBlockKey(blockIdentifier)); err != nil {
277-
return err
278-
}
279-
280-
if err = b.StoreHeadBlockIdentifier(ctx, transaction, block.ParentBlockIdentifier); err != nil {
281-
return err
324+
// Delete block
325+
if err := b.deleteBlock(ctx, transaction, block); err != nil {
326+
return fmt.Errorf("%w: unable to delete block", err)
282327
}
283328

284329
return b.callWorkersAndCommit(ctx, block, transaction, false)
@@ -348,7 +393,7 @@ func (b *BlockStorage) SetNewStartIndex(
348393
currBlock := head
349394
for currBlock.Index >= startIndex {
350395
log.Printf("Removing block %+v\n", currBlock)
351-
block, err := b.GetBlock(ctx, currBlock)
396+
block, err := b.GetBlock(ctx, types.ConstructPartialBlockIdentifier(currBlock))
352397
if err != nil {
353398
return err
354399
}
@@ -373,7 +418,7 @@ func (b *BlockStorage) CreateBlockCache(ctx context.Context) []*types.BlockIdent
373418
}
374419

375420
for len(cache) < syncer.PastBlockSize {
376-
block, err := b.GetBlock(ctx, head)
421+
block, err := b.GetBlock(ctx, types.ConstructPartialBlockIdentifier(head))
377422
if err != nil {
378423
return cache
379424
}
@@ -392,22 +437,22 @@ func (b *BlockStorage) CreateBlockCache(ctx context.Context) []*types.BlockIdent
392437
return cache
393438
}
394439

395-
func (b *BlockStorage) storeBlockHash(
440+
func (b *BlockStorage) storeUniqueKey(
396441
ctx context.Context,
397442
transaction DatabaseTransaction,
398-
block *types.BlockIdentifier,
443+
key []byte,
444+
value []byte,
399445
) error {
400-
hashKey := getBlockHashKey(block)
401-
exists, _, err := transaction.Get(ctx, hashKey)
446+
exists, _, err := transaction.Get(ctx, key)
402447
if err != nil {
403448
return err
404449
}
405450

406451
if exists {
407-
return fmt.Errorf("%w: duplicate block hash %s found", ErrDuplicateBlockHash, block.Hash)
452+
return fmt.Errorf("%w: duplicate key %s found", ErrDuplicateKey, string(key))
408453
}
409454

410-
return transaction.Set(ctx, hashKey, []byte(""))
455+
return transaction.Set(ctx, key, value)
411456
}
412457

413458
func (b *BlockStorage) storeTransactionHash(
@@ -518,7 +563,7 @@ func (b *BlockStorage) FindTransaction(
518563
}
519564
}
520565

521-
blockExists, block, err := txn.Get(ctx, getBlockKey(newestBlock))
566+
blockExists, block, err := txn.Get(ctx, getBlockHashKey(newestBlock.Hash))
522567
if err != nil {
523568
return nil, nil, fmt.Errorf("%w: unable to query database for block", err)
524569
}
@@ -554,15 +599,11 @@ func (b *BlockStorage) AtTip(
554599
ctx context.Context,
555600
tipDelay int64,
556601
) (bool, error) {
557-
head, err := b.GetHeadBlockIdentifier(ctx)
602+
block, err := b.GetBlock(ctx, nil)
558603
if errors.Is(err, ErrHeadBlockNotFound) {
559604
return false, nil
560605
}
561-
if err != nil {
562-
return false, fmt.Errorf("%w: unable to get head block identifir", err)
563-
}
564606

565-
block, err := b.GetBlock(ctx, head)
566607
if err != nil {
567608
return false, fmt.Errorf("%w: unable to get head block", err)
568609
}

0 commit comments

Comments
 (0)