Skip to content

Commit

Permalink
Merge pull request #2170 from OffchainLabs/backup-blob-client
Browse files Browse the repository at this point in the history
Add backup blob client [NIT-1258]
  • Loading branch information
ganeshvanahalli authored Mar 25, 2024
2 parents 5b1871c + daaf7ca commit 703c626
Showing 1 changed file with 61 additions and 29 deletions.
90 changes: 61 additions & 29 deletions util/headerreader/blob_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ import (
)

type BlobClient struct {
ec arbutil.L1Interface
beaconUrl *url.URL
httpClient *http.Client
authorization string
ec arbutil.L1Interface
beaconUrl *url.URL
secondaryBeaconUrl *url.URL
httpClient *http.Client
authorization string

// Filled in in Initialize()
genesisTime uint64
Expand All @@ -41,19 +42,22 @@ type BlobClient struct {
}

type BlobClientConfig struct {
BeaconUrl string `koanf:"beacon-url"`
BlobDirectory string `koanf:"blob-directory"`
Authorization string `koanf:"authorization"`
BeaconUrl string `koanf:"beacon-url"`
SecondaryBeaconUrl string `koanf:"secondary-beacon-url"`
BlobDirectory string `koanf:"blob-directory"`
Authorization string `koanf:"authorization"`
}

var DefaultBlobClientConfig = BlobClientConfig{
BeaconUrl: "",
BlobDirectory: "",
Authorization: "",
BeaconUrl: "",
SecondaryBeaconUrl: "",
BlobDirectory: "",
Authorization: "",
}

func BlobClientAddOptions(prefix string, f *pflag.FlagSet) {
f.String(prefix+".beacon-url", DefaultBlobClientConfig.BeaconUrl, "Beacon Chain RPC URL to use for fetching blobs (normally on port 3500)")
f.String(prefix+".secondary-beacon-url", DefaultBlobClientConfig.SecondaryBeaconUrl, "Backup beacon Chain RPC URL to use for fetching blobs (normally on port 3500) when unable to fetch from primary")
f.String(prefix+".blob-directory", DefaultBlobClientConfig.BlobDirectory, "Full path of the directory to save fetched blobs")
f.String(prefix+".authorization", DefaultBlobClientConfig.Authorization, "Value to send with the HTTP Authorization: header for Beacon REST requests, must include both scheme and scheme parameters")
}
Expand All @@ -63,6 +67,12 @@ func NewBlobClient(config BlobClientConfig, ec arbutil.L1Interface) (*BlobClient
if err != nil {
return nil, fmt.Errorf("failed to parse beacon chain URL: %w", err)
}
var secondaryBeaconUrl *url.URL
if config.SecondaryBeaconUrl != "" {
if secondaryBeaconUrl, err = url.Parse(config.BeaconUrl); err != nil {
return nil, fmt.Errorf("failed to parse secondary beacon chain URL: %w", err)
}
}
if config.BlobDirectory != "" {
if _, err = os.Stat(config.BlobDirectory); err != nil {
if os.IsNotExist(err) {
Expand All @@ -75,11 +85,12 @@ func NewBlobClient(config BlobClientConfig, ec arbutil.L1Interface) (*BlobClient
}
}
return &BlobClient{
ec: ec,
beaconUrl: beaconUrl,
authorization: config.Authorization,
httpClient: &http.Client{},
blobDirectory: config.BlobDirectory,
ec: ec,
beaconUrl: beaconUrl,
secondaryBeaconUrl: secondaryBeaconUrl,
authorization: config.Authorization,
httpClient: &http.Client{},
blobDirectory: config.BlobDirectory,
}, nil
}

Expand All @@ -92,22 +103,43 @@ func beaconRequest[T interface{}](b *BlobClient, ctx context.Context, beaconPath

var empty T

// not really a deep copy, but copies the Path part we care about
url := *b.beaconUrl
url.Path = path.Join(url.Path, beaconPath)

req, err := http.NewRequestWithContext(ctx, "GET", url.String(), http.NoBody)
if err != nil {
return empty, err
}

if b.authorization != "" {
req.Header.Set("Authorization", b.authorization)
fetchData := func(url url.URL) (*http.Response, error) {
url.Path = path.Join(url.Path, beaconPath)
req, err := http.NewRequestWithContext(ctx, "GET", url.String(), http.NoBody)
if err != nil {
return nil, err
}
if b.authorization != "" {
req.Header.Set("Authorization", b.authorization)
}
resp, err := b.httpClient.Do(req)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
bodyStr := string(body)
log.Debug("beacon request returned response with non 200 OK status", "status", resp.Status, "body", bodyStr)
if len(bodyStr) > 100 {
return nil, fmt.Errorf("response returned with status %s, want 200 OK. body: %s ", resp.Status, bodyStr[len(bodyStr)-trailingCharsOfResponse:])
} else {
return nil, fmt.Errorf("response returned with status %s, want 200 OK. body: %s", resp.Status, bodyStr)
}
}
return resp, nil
}

resp, err := b.httpClient.Do(req)
if err != nil {
return empty, err
var resp *http.Response
var err error
if resp, err = fetchData(*b.beaconUrl); err != nil {
if b.secondaryBeaconUrl != nil {
log.Info("error fetching blob data from primary beacon URL, switching to secondary beacon URL", "err", err)
if resp, err = fetchData(*b.secondaryBeaconUrl); err != nil {
return empty, fmt.Errorf("error fetching blob data from secondary beacon URL: %w", err)
}
} else {
return empty, err
}
}
defer resp.Body.Close()

Expand Down

0 comments on commit 703c626

Please sign in to comment.