Skip to content

Commit

Permalink
impl device query http api (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
huangzhiran authored Nov 4, 2024
1 parent 5c5e5e5 commit 283d978
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 0 deletions.
96 changes: 96 additions & 0 deletions api/http.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package api

import (
"encoding/json"
"log/slog"
"net/http"
"strings"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"

Expand All @@ -15,13 +22,102 @@ func newErrResp(err error) *errResp {
return &errResp{Error: err.Error()}
}

type queryReq struct {
DeviceID string `json:"deviceID" binding:"required"`
Signature string `json:"signature,omitempty" binding:"required"`
}

type queryResp struct {
Status int32 `json:"status"`
Owner string `json:"owner"`
Firmware string `json:"firmware,omitempty"`
URI string `json:"uri,omitempty"`
Version string `json:"version,omitempty"`
}

type httpServer struct {
engine *gin.Engine
db *db.DB
}

func (s *httpServer) query(c *gin.Context) {
req := &queryReq{}
if err := c.ShouldBindJSON(req); err != nil {
slog.Error("failed to bind request", "error", err)
c.JSON(http.StatusBadRequest, newErrResp(errors.Wrap(err, "invalid request payload")))
return
}

sigStr := req.Signature
req.Signature = ""

reqJson, err := json.Marshal(req)
if err != nil {
slog.Error("failed to marshal request into json format", "error", err)
c.JSON(http.StatusInternalServerError, newErrResp(errors.Wrap(err, "failed to process request data")))
return
}

sig, err := hexutil.Decode(sigStr)
if err != nil {
slog.Error("failed to decode signature from hex format", "signature", sigStr, "error", err)
c.JSON(http.StatusBadRequest, newErrResp(errors.Wrap(err, "invalid signature format")))
return
}

h := crypto.Keccak256Hash(reqJson)
sigpk, err := crypto.SigToPub(h.Bytes(), sig)
if err != nil {
slog.Error("failed to recover public key from signature", "error", err)
c.JSON(http.StatusBadRequest, newErrResp(errors.Wrap(err, "invalid signature; could not recover public key")))
return
}

owner := crypto.PubkeyToAddress(*sigpk)

d, err := s.db.Device(req.DeviceID)
if err != nil {
slog.Error("failed to query device", "error", err, "device_id", req.DeviceID)
c.JSON(http.StatusInternalServerError, newErrResp(errors.Wrap(err, "failed to query device")))
return
}
if d == nil {
slog.Error("device not exist", "device_id", req.DeviceID)
c.JSON(http.StatusBadRequest, newErrResp(errors.New("device not exist")))
return
}
if d.Owner != owner.String() {
slog.Error("no permission to access the device", "device_id", req.DeviceID)
c.JSON(http.StatusForbidden, newErrResp(errors.New("no permission to access the device")))
return
}

var (
firmware string
uri string
version string
)
if parts := strings.Split(d.RealFirmware, " "); len(parts) == 2 {
app, err := s.db.App(parts[0])
if err != nil {
slog.Error("failed to query app", "error", err, "app_id", parts[0])
c.JSON(http.StatusInternalServerError, newErrResp(errors.Wrap(err, "failed to query app")))
return
}
if app != nil {
firmware = app.ID
uri = app.Uri
version = app.Version
}
}

c.JSON(http.StatusOK, &queryResp{
Status: d.Status,
Owner: d.Owner,
Firmware: firmware,
URI: uri,
Version: version,
})
}

func (s *httpServer) receive(c *gin.Context) {
Expand Down
12 changes: 12 additions & 0 deletions db/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/ethereum/go-ethereum/crypto"
"github.com/pkg/errors"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)

Expand Down Expand Up @@ -58,3 +59,14 @@ func (d *DB) UpsertApp(projectID uint64, name string, key [32]byte, value []byte
}).Create(&t).Error
return errors.Wrap(err, "failed to upsert app")
}

func (d *DB) App(id string) (*App, error) {
t := App{}
if err := d.db.Where("id = ?", id).First(&t).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, errors.Wrap(err, "failed to query app")
}
return &t, nil
}
16 changes: 16 additions & 0 deletions db/device.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package db

import (
"github.com/pkg/errors"
"gorm.io/gorm"
)

const (
CREATED int32 = iota
PROPOSAL
Expand Down Expand Up @@ -32,3 +37,14 @@ type Device struct {
}

func (*Device) TableName() string { return "device" }

func (d *DB) Device(id string) (*Device, error) {
t := Device{}
if err := d.db.Where("id = ?", id).First(&t).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, errors.Wrap(err, "failed to query device")
}
return &t, nil
}

0 comments on commit 283d978

Please sign in to comment.