forked from gligneul/nonodo
-
Notifications
You must be signed in to change notification settings - Fork 13
/
inspect.go
114 lines (99 loc) · 3.01 KB
/
inspect.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Copyright (c) Gabriel de Quadros Ligneul
// SPDX-License-Identifier: Apache-2.0 (see LICENSE)
// This package contains the bindings for the inspect OpenAPI spec.
package inspect
import (
"io"
"net/http"
"net/url"
"time"
"github.com/calindra/nonodo/internal/model"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/labstack/echo/v4"
)
//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen -config=oapi.yaml ../../api/inspect.yaml
// 2^20 bytes, which is the length of the RX buffer in the Cartesi machine.
const PayloadSizeLimit = 1_048_576
// Model is the inspect interface for the nonodo model.
type Model interface {
AddInspectInput(payload []byte) int
GetInspectInput(index int) model.InspectInput
}
// Register the rollup API to echo
func Register(e *echo.Echo, model Model) {
inspectAPI := &inspectAPI{model}
RegisterHandlers(e, inspectAPI)
}
// Shared struct for request handlers.
type inspectAPI struct {
model Model
}
// Handle POST requests to /.
func (a *inspectAPI) InspectPost(c echo.Context) error {
payload, err := io.ReadAll(c.Request().Body)
if err != nil {
return c.String(http.StatusBadRequest, err.Error())
}
if len(payload) > PayloadSizeLimit {
return c.String(http.StatusBadRequest, "Payload reached size limit")
}
return a.inspect(c, payload)
}
// Handle GET requests to /{payload}.
func (a *inspectAPI) Inspect(c echo.Context, _ string) error {
uri := c.Request().RequestURI[9:] // remove '/inspect/'
payload, err := url.QueryUnescape(uri)
if err != nil {
return c.String(http.StatusBadRequest, err.Error())
}
return a.inspect(c, []byte(payload))
}
// Send the inspect input to the model and wait until it is completed.
func (a *inspectAPI) inspect(c echo.Context, payload []byte) error {
// Send inspect to the model
index := a.model.AddInspectInput(payload)
// Poll the model for response
const pollFrequency = 33 * time.Millisecond
ticker := time.NewTicker(pollFrequency)
defer ticker.Stop()
for {
input := a.model.GetInspectInput(index)
if input.Status != model.CompletionStatusUnprocessed {
resp := convertInput(input)
return c.JSON(http.StatusOK, &resp)
}
select {
case <-c.Request().Context().Done():
return c.Request().Context().Err()
case <-ticker.C:
}
}
}
// Convert model input to API type.
func convertInput(input model.InspectInput) InspectResult {
var status CompletionStatus
switch input.Status {
case model.CompletionStatusUnprocessed:
panic("impossible")
case model.CompletionStatusAccepted:
status = Accepted
case model.CompletionStatusRejected:
status = Rejected
case model.CompletionStatusException:
status = Exception
default:
panic("invalid completion status")
}
var reports []Report
for _, report := range input.Reports {
reports = append(reports, Report{
Payload: hexutil.Encode(report.Payload),
})
}
return InspectResult{
Status: status,
Reports: reports,
ExceptionPayload: hexutil.Encode(input.Exception),
ProcessedInputCount: input.ProcessedInputCount,
}
}