Skip to content

Commit

Permalink
Adds pebble db store backend (#80)
Browse files Browse the repository at this point in the history
  • Loading branch information
Ruteri authored Oct 30, 2023
1 parent ce644c7 commit 9b0b88b
Show file tree
Hide file tree
Showing 6 changed files with 205 additions and 0 deletions.
1 change: 1 addition & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ var (
utils.SuaveEthRemoteBackendEndpointFlag,
utils.SuaveConfidentialTransportRedisEndpointFlag,
utils.SuaveConfidentialStoreRedisEndpointFlag,
utils.SuaveConfidentialStorePebbleDbPathFlag,
utils.SuaveEthBundleSigningKeyFlag,
utils.SuaveEthBlockSigningKeyFlag,
utils.SuaveDevModeFlag,
Expand Down
11 changes: 11 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,12 @@ var (
Category: flags.SuaveCategory,
}

SuaveConfidentialStorePebbleDbPathFlag = &cli.StringFlag{
Name: "suave.confidential.pebble-store-db-path",
Usage: "Path to pebble db to use for confidential storage backend (default: local store)",
Category: flags.SuaveCategory,
}

SuaveEthBundleSigningKeyFlag = &cli.StringFlag{
Name: "suave.eth.bundle-signing-key",
EnvVars: []string{"SUAVE_ETH_BUNDLE_SIGNING_KEY"},
Expand Down Expand Up @@ -1700,6 +1706,7 @@ func CheckExclusive(ctx *cli.Context, args ...interface{}) {
}

func SetSuaveConfig(ctx *cli.Context, stack *node.Node, cfg *suave.Config) {
CheckExclusive(ctx, SuaveConfidentialStoreRedisEndpointFlag, SuaveConfidentialStorePebbleDbPathFlag)
if ctx.IsSet(SuaveEthRemoteBackendEndpointFlag.Name) {
cfg.SuaveEthRemoteBackendEndpoint = ctx.String(SuaveEthRemoteBackendEndpointFlag.Name)
}
Expand All @@ -1712,6 +1719,10 @@ func SetSuaveConfig(ctx *cli.Context, stack *node.Node, cfg *suave.Config) {
cfg.RedisStoreUri = ctx.String(SuaveConfidentialStoreRedisEndpointFlag.Name)
}

if ctx.IsSet(SuaveConfidentialStorePebbleDbPathFlag.Name) {
cfg.PebbleDbPath = ctx.String(SuaveConfidentialStorePebbleDbPathFlag.Name)
}

if ctx.IsSet(SuaveEthBundleSigningKeyFlag.Name) {
cfg.EthBundleSigningKeyHex = ctx.String(SuaveEthBundleSigningKeyFlag.Name)
}
Expand Down
5 changes: 5 additions & 0 deletions eth/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,11 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if err != nil {
return nil, err
}
} else if config.Suave.PebbleDbPath != "" {
confidentialStoreBackend, err = cstore.NewPebbleStoreBackend(config.Suave.PebbleDbPath)
if err != nil {
return nil, err
}
} else {
confidentialStoreBackend = cstore.NewLocalConfidentialStore()
}
Expand Down
1 change: 1 addition & 0 deletions suave/core/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ type Config struct {
SuaveEthRemoteBackendEndpoint string
RedisStorePubsubUri string
RedisStoreUri string
PebbleDbPath string
EthBundleSigningKeyHex string
EthBlockSigningKeyHex string
}
Expand Down
176 changes: 176 additions & 0 deletions suave/cstore/pebble_store_backend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package cstore

import (
"context"
"encoding/json"
"errors"
"fmt"

"github.com/cockroachdb/pebble"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
suave "github.com/ethereum/go-ethereum/suave/core"
)

var (
formatPebbleBidKey = formatRedisBidKey
formatPebbleBidValueKey = formatRedisBidValueKey
)

type PebbleStoreBackend struct {
ctx context.Context
cancel context.CancelFunc
dbPath string
db *pebble.DB
}

var bidByBlockAndProtocolIndexDbKey = func(blockNumber uint64, namespace string) []byte {
return []byte(fmt.Sprintf("bids-block-%d-ns-%s", blockNumber, namespace))
}

type bidByBlockAndProtocolIndexType = []types.BidId

func NewPebbleStoreBackend(dbPath string) (*PebbleStoreBackend, error) {
// TODO: should we check sanity in the constructor?
backend := &PebbleStoreBackend{
dbPath: dbPath,
}

return backend, backend.start()
}

func (b *PebbleStoreBackend) start() error {
if b.cancel != nil {
b.cancel()
}

ctx, cancel := context.WithCancel(context.Background())
b.cancel = cancel
b.ctx = ctx

db, err := pebble.Open(b.dbPath, &pebble.Options{})
if err != nil {
return fmt.Errorf("could not open pebble database at %s: %w", b.dbPath, err)
}

go func() {
<-ctx.Done()
db.Close()
}()

b.db = db

return nil
}

func (b *PebbleStoreBackend) Stop() error {
b.cancel()
return nil
}

func (b *PebbleStoreBackend) InitializeBid(bid suave.Bid) error {
key := []byte(formatPebbleBidKey(bid.Id))

_, closer, err := b.db.Get(key)
if !errors.Is(err, pebble.ErrNotFound) {
if err == nil {
closer.Close()
}
return suave.ErrBidAlreadyPresent
}

data, err := json.Marshal(bid)
if err != nil {
return err
}

err = b.db.Set(key, data, nil)
if err != nil {
return err
}

// index update
var currentValues bidByBlockAndProtocolIndexType

dbBlockProtoIndexKey := bidByBlockAndProtocolIndexDbKey(bid.DecryptionCondition, bid.Version)
rawCurrentValues, closer, err := b.db.Get(dbBlockProtoIndexKey)
if err != nil {
if !errors.Is(err, pebble.ErrNotFound) {
return err
}
} else if err == nil {
err = json.Unmarshal(rawCurrentValues, &currentValues)
closer.Close()
if err != nil {
return err
}
}

currentValues = append(currentValues, bid.Id)
rawUpdatedValues, err := json.Marshal(currentValues)
if err != nil {
return err
}

return b.db.Set(dbBlockProtoIndexKey, rawUpdatedValues, nil)
}

func (b *PebbleStoreBackend) FetchBidById(bidId suave.BidId) (suave.Bid, error) {
key := []byte(formatPebbleBidKey(bidId))

bidData, closer, err := b.db.Get(key)
if err != nil {
return suave.Bid{}, fmt.Errorf("bid %x not found: %w", bidId, err)
}

var bid suave.Bid
err = json.Unmarshal(bidData, &bid)
closer.Close()
if err != nil {
return suave.Bid{}, fmt.Errorf("could not unmarshal stored bid: %w", err)
}

return bid, nil
}

func (b *PebbleStoreBackend) Store(bid suave.Bid, caller common.Address, key string, value []byte) (suave.Bid, error) {
storeKey := []byte(formatPebbleBidValueKey(bid.Id, key))
return bid, b.db.Set(storeKey, value, nil)
}

func (b *PebbleStoreBackend) Retrieve(bid suave.Bid, caller common.Address, key string) ([]byte, error) {
storeKey := []byte(formatPebbleBidValueKey(bid.Id, key))
data, closer, err := b.db.Get(storeKey)
if err != nil {
return nil, fmt.Errorf("could not fetch data for bid %x and key %s: %w", bid.Id, key, err)
}
ret := make([]byte, len(data))
copy(ret, data)
closer.Close()
return ret, nil
}

func (b *PebbleStoreBackend) FetchBidsByProtocolAndBlock(blockNumber uint64, namespace string) []suave.Bid {
dbBlockProtoIndexKey := bidByBlockAndProtocolIndexDbKey(blockNumber, namespace)
rawCurrentValues, closer, err := b.db.Get(dbBlockProtoIndexKey)
if err != nil {
return nil
}

var currentBidIds bidByBlockAndProtocolIndexType
err = json.Unmarshal(rawCurrentValues, &currentBidIds)
closer.Close()
if err != nil {
return nil
}

bids := []suave.Bid{}
for _, bidId := range currentBidIds {
bid, err := b.FetchBidById(bidId)
if err == nil {
bids = append(bids, bid)
}
}

return bids
}
11 changes: 11 additions & 0 deletions suave/cstore/pebble_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package cstore

import (
"testing"
)

func TestPebbleStore(t *testing.T) {
tmpDir := t.TempDir()
store, _ := NewPebbleStoreBackend(tmpDir)
testBackendStore(t, store)
}

0 comments on commit 9b0b88b

Please sign in to comment.