Skip to content

Commit

Permalink
feat: Fault Testing Support
Browse files Browse the repository at this point in the history
  • Loading branch information
EthenNotEthan committed Jun 20, 2024
1 parent ceff6e8 commit ee68028
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 20 deletions.
7 changes: 6 additions & 1 deletion client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ const (

// TODO: Add support for custom http client option
type Config struct {
URL string
Actor string
URL string
}

// ProxyClient is an interface for communicating with the EigenDA proxy server
Expand Down Expand Up @@ -75,6 +76,10 @@ func (c *client) GetData(ctx context.Context, cert *common.Certificate, domain c

url := fmt.Sprintf("%s/get/0x%x?domain=%s", c.cfg.URL, b, domain.String())

if c.cfg.Actor != "" {
url = fmt.Sprintf("%s&actor=%s", url, c.cfg.Actor)
}

req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("failed to construct http request: %e", err)
Expand Down
42 changes: 42 additions & 0 deletions fault/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package fault

import (
"encoding/json"
"os"
"time"
)

type Mode int

const (
Honest = iota
Byzantine
IntervalByzantine
)

// Behavior represents a system actor return behavior within a L2 (e.g, sequencer, challenger, etc)
type Behavior struct {
Mode Mode `json:"mode"`
Interval time.Duration `json:"interval"` // only used for IntervalByzantine
}

type Config struct {
Actors map[string]Behavior
}

func LoadConfig(path string) (*Config, error) {
jsonFile, err := os.Open(path)
if err != nil {
return nil, err
}

defer jsonFile.Close()

var config Config
err = json.NewDecoder(jsonFile).Decode(&config)
if err != nil {
return nil, err
}

return &config, nil
}
14 changes: 13 additions & 1 deletion server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,19 @@ func (svr *Server) Health(w http.ResponseWriter, r *http.Request) error {
}

func (svr *Server) HandleGet(w http.ResponseWriter, r *http.Request) error {
ctx := context.Background()

domain, err := ReadDomainFilter(r)
if err != nil {
svr.WriteBadRequest(w, invalidDomain)
return err
}

actor := ReadActor(r)
if actor != "" {
ctx = context.WithValue(ctx, "actor", actor)
}

key := path.Base(r.URL.Path)
comm, err := eigenda.StringToCommit(key)
if err != nil {
Expand All @@ -156,7 +163,7 @@ func (svr *Server) HandleGet(w http.ResponseWriter, r *http.Request) error {
return err
}

input, err := svr.store.Get(r.Context(), comm, domain)
input, err := svr.store.Get(ctx, comm, domain)
if err != nil && errors.Is(err, ErrNotFound) {
svr.WriteNotFound(w, err.Error())
return err
Expand Down Expand Up @@ -224,6 +231,11 @@ func ReadDomainFilter(r *http.Request) (common.DomainType, error) {
return dt, nil
}

func ReadActor(r *http.Request) string {
query := r.URL.Query()
return query.Get("actor")
}

func (svr *Server) Store() store.Store {
return svr.store
}
Expand Down
100 changes: 82 additions & 18 deletions store/memory.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/ethereum/go-ethereum/rlp"

"github.com/Layr-Labs/eigenda-proxy/common"
eigendacommon "github.com/Layr-Labs/eigenda-proxy/common"
"github.com/Layr-Labs/eigenda-proxy/fault"
"github.com/Layr-Labs/eigenda-proxy/verify"
"github.com/Layr-Labs/eigenda/api/clients/codecs"
grpccommon "github.com/Layr-Labs/eigenda/api/grpc/common"
Expand All @@ -23,13 +23,15 @@ import (
const (
MemStoreFlagName = "memstore.enabled"
ExpirationFlagName = "memstore.expiration"
FaultFlagName = "memstore.fault-config"

DefaultPruneInterval = 500 * time.Millisecond
)

type MemStoreConfig struct {
Enabled bool
BlobExpiration time.Duration
FaultCfg *fault.Config
}

// MemStore is a simple in-memory store for blobs which uses an expiration
Expand Down Expand Up @@ -102,33 +104,20 @@ func (e *MemStore) pruneExpired() {
}

// Get fetches a value from the store.
func (e *MemStore) Get(ctx context.Context, commit []byte, domain eigendacommon.DomainType) ([]byte, error) {
func (e *MemStore) Get(ctx context.Context, commit []byte, domain common.DomainType) ([]byte, error) {
e.reads += 1
e.Lock()
defer e.Unlock()

var cert common.Certificate
err := rlp.DecodeBytes(commit, &cert)
if err != nil {
return nil, fmt.Errorf("failed to decode DA cert to RLP format: %w", err)
}

var encodedBlob []byte
var exists bool
if encodedBlob, exists = e.store[string(commit)]; !exists {
return nil, fmt.Errorf("commitment key not found")
}

// Don't need to do this really since it's a mock store
err = e.verifier.VerifyCommitment(cert.BlobHeader.Commitment, encodedBlob)
encodedBlob, err := e.fetch(ctx, commit)
if err != nil {
return nil, err
}

switch domain {
case eigendacommon.BinaryDomain:
case common.BinaryDomain:
return e.codec.DecodeBlob(encodedBlob)
case eigendacommon.PolyDomain:
case common.PolyDomain:
return encodedBlob, nil
default:
return nil, fmt.Errorf("unexpected domain type: %d", domain)
Expand Down Expand Up @@ -209,6 +198,81 @@ func (e *MemStore) Put(ctx context.Context, value []byte) ([]byte, error) {
return certBytes, nil
}

func (e *MemStore) honestFetch(commit []byte) ([]byte, error) {
var cert common.Certificate
err := rlp.DecodeBytes(commit, &cert)
if err != nil {
return nil, fmt.Errorf("failed to decode DA cert to RLP format: %w", err)
}

var encodedBlob []byte
var exists bool
if encodedBlob, exists = e.store[string(commit)]; !exists {
return nil, fmt.Errorf("commitment key not found")
}

// Don't need to do this really since it's a mock store
err = e.verifier.VerifyCommitment(cert.BlobHeader.Commitment, encodedBlob)
if err != nil {
return nil, err
}

return encodedBlob, nil
}

func (e *MemStore) byzantineFetch(commit []byte) ([]byte, error) {
var cert common.Certificate
err := rlp.DecodeBytes(commit, &cert)
if err != nil {
return nil, fmt.Errorf("failed to decode DA cert to RLP format: %w", err)
}

var encodedBlob []byte
var exists bool
if encodedBlob, exists = e.store[string(commit)]; !exists {
return nil, fmt.Errorf("commitment key not found")
}

// flip a bit in the bob to simulate Byzantine behavior
encodedBlob[0] = ^encodedBlob[0]

return encodedBlob, nil
}

func (e *MemStore) fetch(ctx context.Context, commit []byte) ([]byte, error) {
if e.cfg.FaultCfg == nil {
return e.honestFetch(commit)
}

actor := ctx.Value("actor").(string)

if actor == "" {
return nil, fmt.Errorf("actor not found in context")
}

if _, exists := e.cfg.FaultCfg.Actors[actor]; !exists {
return nil, fmt.Errorf("actor not found in fault config")
}

behavior := e.cfg.FaultCfg.Actors[actor]

switch behavior.Mode {
case fault.Honest:
return e.honestFetch(commit)
case fault.Byzantine:
return e.byzantineFetch(commit)

case fault.IntervalByzantine:
if e.reads%int(behavior.Interval) == 0 {
return e.byzantineFetch(commit)
}

return e.honestFetch(commit)
default:
return nil, fmt.Errorf("unexpected actor mode: %d", behavior.Mode)
}
}

func (e *MemStore) Stats() *common.Stats {
e.RLock()
defer e.RUnlock()
Expand Down

0 comments on commit ee68028

Please sign in to comment.