diff --git a/api/fiber.go b/api/fiber.go index 5912223..275b5d9 100644 --- a/api/fiber.go +++ b/api/fiber.go @@ -2,8 +2,10 @@ package api import ( "encoding/json" + "fmt" "io" "net/http" + "strconv" ) type FiberAPI struct { @@ -16,6 +18,12 @@ func NewFiberAPI(url string, apiKey string) *FiberAPI { return &FiberAPI{url, apiKey} } +type Response[T any] struct { + Status string `json:"status"` + Message T `json:"message"` + Error string `json:"error"` +} + type Quota struct { EgressMB int `json:"egress_mb"` MaxEgressMB int `json:"max_egress_mb"` @@ -24,14 +32,19 @@ type Quota struct { MaxActiveStreams int `json:"max_active_streams"` } +func (f *FiberAPI) prepareRequest(req *http.Request) { + req.Header.Set("x-api-key", f.apiKey) + req.Header.Set("Content-Type", "application/json") +} + func (f *FiberAPI) GetQuota() (*Quota, error) { req, err := http.NewRequest("GET", f.url+"/quota", nil) if err != nil { return nil, err } - req.Header.Set("x-api-key", f.apiKey) - req.Header.Set("Content-Type", "application/json") + f.prepareRequest(req) + fmt.Println(req.Header) res, err := http.DefaultClient.Do(req) if err != nil { @@ -45,10 +58,63 @@ func (f *FiberAPI) GetQuota() (*Quota, error) { return nil, err } - quota := new(Quota) - if err := json.Unmarshal(body, quota); err != nil { + response := new(Response[*Quota]) + if err := json.Unmarshal(body, response); err != nil { + return nil, err + } + + if response.Status != "success" { + return nil, fmt.Errorf("Error getting quota: %s", response.Error) + } + + return response.Message, nil +} + +type TraceEntry struct { + Timestamp uint64 `json:"timestamp"` + NodeID string `json:"node_id"` + Region string `json:"region"` + Source string `json:"source"` + ObservationType string `json:"observation_type"` +} + +func (f *FiberAPI) TraceTransaction(hash, observationType string, private bool) ([]*TraceEntry, error) { + req, err := http.NewRequest("GET", f.url+"/trace/tx/"+hash, nil) + if err != nil { return nil, err } - return quota, nil + if observationType == "" { + observationType = "all" + } + + q := req.URL.Query() + q.Add("observation_type", observationType) + q.Add("private", strconv.FormatBool(private)) + req.URL.RawQuery = q.Encode() + + f.prepareRequest(req) + + res, err := http.DefaultClient.Do(req) + if err != nil { + return nil, err + } + + defer res.Body.Close() + + body, err := io.ReadAll(res.Body) + if err != nil { + return nil, err + } + + response := new(Response[[]*TraceEntry]) + if err := json.Unmarshal(body, response); err != nil { + return nil, err + } + + if response.Status != "success" { + return nil, fmt.Errorf("Error getting quota: %s", response.Error) + } + + return response.Message, nil } diff --git a/api/fiber_test.go b/api/fiber_test.go deleted file mode 100644 index 96e6324..0000000 --- a/api/fiber_test.go +++ /dev/null @@ -1,5 +0,0 @@ -package api - -func testQuota() { - -} diff --git a/cli.go b/cli.go index 13d7b6c..d288249 100644 --- a/cli.go +++ b/cli.go @@ -31,14 +31,7 @@ func NewApp() *cli.App { }, Action: func(c *cli.Context) error { key := c.String("key") - if key == "" { - return fmt.Errorf("API key not provided") - } - url := c.String("url") - if url == "" { - return fmt.Errorf("URL not provided") - } path, err := inititalize(url, key) if err != nil { @@ -79,6 +72,63 @@ func NewApp() *cli.App { return nil }, }, + { + Name: "trace", + Usage: "Trace transactions or blocks", + Subcommands: []*cli.Command{ + { + Name: "tx", + Usage: "Trace a transaction", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "hash", + Aliases: []string{"H"}, + Usage: "The transaction hash to trace", + Required: true, + }, + &cli.StringFlag{ + Name: "type", + Aliases: []string{"t"}, + Usage: "The observation type to trace (p2p | fiber | all)", + Value: "all", + }, + &cli.BoolFlag{ + Name: "private", + Aliases: []string{"p"}, + Usage: "Whether or not the transaction is private (sent with your API key)", + Value: false, + }, + &cli.BoolFlag{ + Name: "show-source", + Aliases: []string{"s"}, + Usage: "Whether or not to show the source of the transaction", + Value: false, + }, + }, + Action: func(c *cli.Context) error { + cfg, err := ReadConfig() + if err != nil { + return fmt.Errorf("Error reading config, did you run cbctl init?: %w", err) + } + + hash := c.String("hash") + observationType := c.String("type") + private := c.Bool("private") + showSource := c.Bool("show-source") + + api := api.NewFiberAPI(cfg.Url, cfg.ApiKey) + traces, err := api.TraceTransaction(hash, observationType, private) + if err != nil { + return fmt.Errorf("Error getting quota: %w", err) + } + + printTransactionTrace(traces, showSource) + + return nil + }, + }, + }, + }, }, }, }, diff --git a/config.go b/config.go index 382bed8..8711801 100644 --- a/config.go +++ b/config.go @@ -7,8 +7,8 @@ import ( ) type Config struct { - Url string - ApiKey string + Url string `toml:"url"` + ApiKey string `toml:"api_key"` } func ReadConfig() (*Config, error) {