-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from machinefi/ioid-with-http
add ioid & device query http api
- Loading branch information
Showing
19 changed files
with
1,935 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,22 @@ | ||
FROM --platform=linux/amd64 golang:1.22 AS builder | ||
FROM --platform=linux/amd64 golang:1.22-alpine AS builder | ||
Check warning on line 1 in cmd/sequencer/Dockerfile
|
||
|
||
ENV GO111MODULE=on | ||
|
||
RUN apk update && apk upgrade && apk add --no-cache ca-certificates tzdata musl-dev gcc && update-ca-certificates | ||
|
||
WORKDIR /go/src | ||
COPY ./ ./ | ||
|
||
RUN cd ./cmd/sequencer && make build | ||
RUN cd ./cmd/sequencer && CGO_ENABLED=1 CGO_LDFLAGS='-L./lib/linux-x86_64 -lioConnectCore' go build -ldflags "-s -w -extldflags '-static'" -o pebble-sequencer | ||
|
||
FROM --platform=linux/amd64 alpine:3.20 AS runtime | ||
Check warning on line 12 in cmd/sequencer/Dockerfile
|
||
|
||
ENV LANG en_US.UTF-8 | ||
Check warning on line 14 in cmd/sequencer/Dockerfile
|
||
|
||
FROM --platform=linux/amd64 scratch AS runtime | ||
RUN apk add --no-cache ca-certificates tzdata | ||
|
||
COPY --from=builder /lib64/ld-linux-x86-64.so.2 /lib64/ld-linux-x86-64.so.2 | ||
COPY --from=builder /lib/x86_64-linux-gnu/libc.so.6 /lib/x86_64-linux-gnu/libc.so.6 | ||
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ | ||
COPY --from=builder /go/src/cmd/sequencer/pebble-sequencer /go/bin/pebble-sequencer | ||
EXPOSE 80 | ||
EXPOSE 9000 | ||
|
||
WORKDIR /go/bin | ||
ENTRYPOINT ["/go/bin/pebble-sequencer"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
package api | ||
|
||
import ( | ||
"context" | ||
"log/slog" | ||
"net/http" | ||
"strings" | ||
|
||
"github.com/gin-gonic/gin" | ||
"github.com/machinefi/ioconnect-go/pkg/ioconnect" | ||
"github.com/pkg/errors" | ||
|
||
"github.com/machinefi/sprout-pebble-sequencer/cmd/sequencer/apitypes" | ||
"github.com/machinefi/sprout-pebble-sequencer/cmd/sequencer/clients" | ||
"github.com/machinefi/sprout-pebble-sequencer/pkg/models" | ||
"github.com/machinefi/sprout-pebble-sequencer/pkg/modules/event" | ||
) | ||
|
||
type httpServer struct { | ||
ctx context.Context | ||
engine *gin.Engine | ||
jwk *ioconnect.JWK | ||
clients *clients.Manager | ||
} | ||
|
||
func NewHttpServer(ctx context.Context, jwk *ioconnect.JWK, clientMgr *clients.Manager) *httpServer { | ||
s := &httpServer{ | ||
ctx: ctx, | ||
engine: gin.Default(), | ||
jwk: jwk, | ||
clients: clientMgr, | ||
} | ||
|
||
slog.Debug("jwk information", | ||
"did:io", jwk.DID(), | ||
"did:io#key", jwk.KID(), | ||
"ka did:io", jwk.KeyAgreementDID(), | ||
"ka did:io#key", jwk.KeyAgreementKID(), | ||
"doc", jwk.Doc(), | ||
) | ||
|
||
s.engine.POST("/issue_vc", s.issueJWTCredential) | ||
s.engine.POST("/device/:imei/confirm", s.verifyToken, s.confirmDevice) | ||
s.engine.GET("/device/:imei/query", s.verifyToken, s.queryDeviceState) | ||
s.engine.GET("/didDoc", s.didDoc) | ||
|
||
return s | ||
} | ||
|
||
// this func will block caller | ||
func (s *httpServer) Run(address string) error { | ||
if err := s.engine.Run(address); err != nil { | ||
return errors.Wrap(err, "failed to start http server") | ||
} | ||
return nil | ||
} | ||
|
||
// verifyToken make sure the client token is issued by sequencer | ||
func (s *httpServer) verifyToken(c *gin.Context) { | ||
tok := c.GetHeader("Authorization") | ||
if tok == "" { | ||
tok = c.Query("authorization") | ||
} | ||
|
||
if tok == "" { | ||
return | ||
} | ||
|
||
tok = strings.TrimSpace(strings.Replace(tok, "Bearer", " ", 1)) | ||
|
||
clientID, err := s.jwk.VerifyToken(tok) | ||
if err != nil { | ||
c.JSON(http.StatusUnauthorized, apitypes.NewErrRsp(errors.Wrap(err, "invalid credential token"))) | ||
return | ||
} | ||
client := s.clients.ClientByIoID(clientID) | ||
if client == nil { | ||
c.JSON(http.StatusUnauthorized, apitypes.NewErrRsp(errors.New("invalid credential token"))) | ||
return | ||
} | ||
|
||
ctx := clients.WithClientID(c.Request.Context(), client) | ||
c.Request = c.Request.WithContext(ctx) | ||
} | ||
|
||
func (s *httpServer) confirmDevice(c *gin.Context) { | ||
//imei := c.Param("imei") | ||
|
||
} | ||
|
||
func (s *httpServer) queryDeviceState(c *gin.Context) { | ||
imei := c.Param("imei") | ||
|
||
dev := &models.Device{ID: imei} | ||
if err := event.FetchByPrimary(s.ctx, dev); err != nil { | ||
c.JSON(http.StatusInternalServerError, apitypes.NewErrRsp(err)) | ||
return | ||
} | ||
if dev.Status == models.CREATED { | ||
c.JSON(http.StatusBadRequest, apitypes.NewErrRsp(errors.Errorf("device %s is not propsaled", dev.ID))) | ||
return | ||
} | ||
var ( | ||
firmware string | ||
uri string | ||
version string | ||
) | ||
if parts := strings.Split(dev.RealFirmware, " "); len(parts) == 2 { | ||
app := &models.App{ID: parts[0]} | ||
err := event.FetchByPrimary(s.ctx, app) | ||
if err == nil { | ||
firmware = app.ID | ||
uri = app.Uri | ||
version = app.Version | ||
} | ||
} | ||
|
||
// meta := contexts.AppMeta().MustFrom(ctx) | ||
//pubType := "pub_DeviceQueryRsp" | ||
pubData := &struct { | ||
Status int32 `json:"status"` | ||
Proposer string `json:"proposer"` | ||
Firmware string `json:"firmware,omitempty"` | ||
URI string `json:"uri,omitempty"` | ||
Version string `json:"version,omitempty"` | ||
ServerMeta string `json:"server_meta,omitempty"` | ||
}{ | ||
Status: dev.Status, | ||
Proposer: dev.Proposer, | ||
Firmware: firmware, | ||
URI: uri, | ||
Version: version, | ||
// ServerMeta: meta.String(), | ||
} | ||
|
||
// if client != nil { | ||
// slog.Info("encrypt response task query", "response", response) | ||
// cipher, err := s.jwk.EncryptJSON(response, client.KeyAgreementKID()) | ||
// if err != nil { | ||
// c.JSON(http.StatusInternalServerError, apitypes.NewErrRsp(errors.Wrap(err, "failed to encrypt response when query task"))) | ||
// return | ||
// } | ||
// c.Data(http.StatusOK, "application/octet-stream", cipher) | ||
// return | ||
// } | ||
|
||
c.JSON(http.StatusOK, pubData) | ||
} | ||
|
||
func (s *httpServer) didDoc(c *gin.Context) { | ||
if s.jwk == nil { | ||
c.JSON(http.StatusNotAcceptable, apitypes.NewErrRsp(errors.New("jwk is not config"))) | ||
return | ||
} | ||
c.JSON(http.StatusOK, s.jwk.Doc()) | ||
} | ||
|
||
func (s *httpServer) issueJWTCredential(c *gin.Context) { | ||
req := new(apitypes.IssueTokenReq) | ||
if err := c.ShouldBindJSON(req); err != nil { | ||
c.JSON(http.StatusBadRequest, apitypes.NewErrRsp(err)) | ||
return | ||
} | ||
|
||
client := s.clients.ClientByIoID(req.ClientID) | ||
if client == nil { | ||
c.String(http.StatusForbidden, errors.Errorf("client is not register to ioRegistry").Error()) | ||
return | ||
} | ||
|
||
token, err := s.jwk.SignToken(req.ClientID) | ||
if err != nil { | ||
c.String(http.StatusInternalServerError, errors.Wrap(err, "failed to sign token").Error()) | ||
return | ||
} | ||
slog.Info("token signed", "token", token) | ||
|
||
cipher, err := s.jwk.Encrypt([]byte(token), client.KeyAgreementKID()) | ||
if err != nil { | ||
c.String(http.StatusInternalServerError, errors.Wrap(err, "failed to encrypt").Error()) | ||
return | ||
} | ||
|
||
c.Data(http.StatusOK, "application/json", cipher) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package apitypes | ||
|
||
import "time" | ||
|
||
type ErrRsp struct { | ||
Error string `json:"error,omitempty"` | ||
} | ||
|
||
func NewErrRsp(err error) *ErrRsp { | ||
return &ErrRsp{Error: err.Error()} | ||
} | ||
|
||
type HandleMessageReq struct { | ||
ProjectID uint64 `json:"projectID" binding:"required"` | ||
ProjectVersion string `json:"projectVersion" binding:"required"` | ||
Data string `json:"data" binding:"required"` | ||
} | ||
|
||
type HandleMessageRsp struct { | ||
MessageID string `json:"messageID"` | ||
} | ||
|
||
type LivenessRsp struct { | ||
Status string `json:"status"` | ||
} | ||
|
||
type StateLog struct { | ||
State string `json:"state"` | ||
Time time.Time `json:"time"` | ||
Comment string `json:"comment"` | ||
Result string `json:"result"` | ||
} | ||
|
||
type QueryTaskStateLogRsp struct { | ||
TaskID uint64 `json:"taskID"` | ||
ProjectID uint64 `json:"projectID"` | ||
States []*StateLog `json:"states"` | ||
} | ||
|
||
type QueryMessageStateLogRsp struct { | ||
MessageID string `json:"messageID"` | ||
States []*StateLog `json:"states"` | ||
} | ||
|
||
type CoordinatorConfigRsp struct { | ||
ProjectContractAddress string `json:"projectContractAddress"` | ||
OperatorETHAddress string `json:"OperatorETHAddress,omitempty"` | ||
OperatorSolanaAddress string `json:"operatorSolanaAddress,omitempty"` | ||
} | ||
|
||
type IssueTokenReq struct { | ||
ClientID string `json:"clientID"` | ||
} | ||
|
||
type IssueTokenRsp struct { | ||
Token string `json:"token"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package apitypes | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestNewErrRsp(t *testing.T) { | ||
r := require.New(t) | ||
rsp := NewErrRsp(errors.New(t.Name())) | ||
r.Contains(rsp.Error, t.Name()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package clients | ||
|
||
import ( | ||
"github.com/machinefi/ioconnect-go/pkg/ioconnect" | ||
) | ||
|
||
type Client struct { | ||
jwk *ioconnect.JWK | ||
} | ||
|
||
func (c *Client) KeyAgreementKID() string { | ||
return c.jwk.KeyAgreementKID() | ||
} | ||
|
||
func (c *Client) DID() string { | ||
return c.jwk.DID() | ||
} | ||
|
||
func (c *Client) Doc() *ioconnect.Doc { | ||
return c.jwk.Doc() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package clients | ||
|
||
import "context" | ||
|
||
type clientIDCtxKey struct{} | ||
|
||
func WithClientID(parent context.Context, client *Client) context.Context { | ||
if parent == nil { | ||
panic("with client id context, nil context") | ||
} | ||
return context.WithValue(parent, clientIDCtxKey{}, client) | ||
} | ||
|
||
func ClientIDFrom(ctx context.Context) *Client { | ||
v, _ := ctx.Value(clientIDCtxKey{}).(*Client) | ||
return v | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
.DEFAULT_GOAL := update | ||
|
||
.PHONY: update | ||
update: fetch generate_abi | ||
|
||
.PHONY: fetch | ||
fetch: | ||
@echo "#### clone ioID-contracts main branch..." | ||
@if [ -d "ioid" ]; then \ | ||
cd ioid && git pull --quiet origin main; \ | ||
else \ | ||
git clone -b main --quiet git@github.com:machinefi/ioID-contracts.git ioid; \ | ||
fi | ||
@echo DONE | ||
|
||
.PHONY: generate_abi | ||
generate_abi: generate_abi_ioid generate_abi_project_device | ||
|
||
.PHONY: generate_abi_ioid | ||
generate_abi_ioid: | ||
@echo "#### generate abis of ioID from latest contracts..." | ||
@cd ioid && yarn install > /dev/null 2>&1 && yarn hardhat compile > /dev/null 2>&1 | ||
@cat ioid/artifacts/contracts/ioIDRegistry.sol/ioIDRegistry.json | jq .abi > ioIDRegistry.json | ||
@echo DONE | ||
|
||
.PHONY: generate_abi_project_device | ||
generate_abi_project_device: | ||
@echo "#### generate abis of ProjectDevice from latest contracts..." | ||
@cd ../../smartcontracts && yarn install > /dev/null 2>&1 && yarn hardhat compile > /dev/null 2>&1 | ||
@cat ../../smartcontracts/artifacts/contracts/examples/ProjectDevice.sol/ProjectDevice.json | jq .abi > ProjectDevice.json | ||
@cat ../../smartcontracts/artifacts/contracts/W3bstreamProject.sol/W3bstreamProject.json | jq .abi > W3bstreamProject.json | ||
@echo DONE | ||
|
||
.PHONY: clean | ||
clean: | ||
@rm -rf ioid | ||
|
Oops, something went wrong.