/
callview.go
146 lines (125 loc) · 5.27 KB
/
callview.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package state
import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"net/http"
"github.com/labstack/echo/v4"
"github.com/pangpanglabs/echoswagger/v2"
"github.com/iotaledger/wasp/packages/chain/chainutil"
"github.com/iotaledger/wasp/packages/chains"
"github.com/iotaledger/wasp/packages/isc"
"github.com/iotaledger/wasp/packages/isc/coreutil"
"github.com/iotaledger/wasp/packages/kv"
"github.com/iotaledger/wasp/packages/kv/dict"
"github.com/iotaledger/wasp/packages/kv/optimism"
"github.com/iotaledger/wasp/packages/webapi/httperrors"
"github.com/iotaledger/wasp/packages/webapi/routes"
)
type callViewService struct {
chains chains.Provider
}
func AddEndpoints(server echoswagger.ApiRouter, allChains chains.Provider) {
dictExample := dict.Dict{
"key1": []byte("value1"),
}.JSONDict()
s := &callViewService{allChains}
server.POST(routes.CallViewByName(":chainID", ":contractHname", ":fname"), s.handleCallViewByName).
SetSummary("Call a view function on a contract by name").
AddParamPath("", "chainID", "ChainID (base58-encoded)").
AddParamPath("", "contractHname", "Contract Hname").
AddParamPath("getInfo", "fname", "Function name").
AddParamBody(dictExample, "params", "Parameters", false).
AddResponse(http.StatusOK, "Result", dictExample, nil)
server.GET(routes.CallViewByName(":chainID", ":contractHname", ":fname"), s.handleCallViewByName).
SetSummary("Call a view function on a contract by name").
AddParamPath("", "chainID", "ChainID (base58-encoded)").
AddParamPath("", "contractHname", "Contract Hname").
AddParamPath("getInfo", "fname", "Function name").
AddParamBody(dictExample, "params", "Parameters", false).
AddResponse(http.StatusOK, "Result", dictExample, nil)
server.POST(routes.CallViewByHname(":chainID", ":contractHname", ":functionHname"), s.handleCallViewByHname).
SetSummary("Call a view function on a contract by Hname").
AddParamPath("", "chainID", "ChainID (base58-encoded)").
AddParamPath("", "contractHname", "Contract Hname").
AddParamPath("getInfo", "functionHname", "Function Hname").
AddParamBody(dictExample, "params", "Parameters", false).
AddResponse(http.StatusOK, "Result", dictExample, nil)
server.GET(routes.CallViewByHname(":chainID", ":contractHname", ":functionHname"), s.handleCallViewByHname).
SetSummary("Call a view function on a contract by Hname").
AddParamPath("", "chainID", "ChainID (base58-encoded)").
AddParamPath("", "contractHname", "Contract Hname").
AddParamPath("getInfo", "functionHname", "Function Hname").
AddParamBody(dictExample, "params", "Parameters", false).
AddResponse(http.StatusOK, "Result", dictExample, nil)
server.GET(routes.StateGet(":chainID", ":key"), s.handleStateGet).
SetSummary("Fetch the raw value associated with the given key in the chain state").
AddParamPath("", "chainID", "ChainID (base58-encoded)").
AddParamPath("", "key", "Key (hex-encoded)").
AddResponse(http.StatusOK, "Result", []byte("value"), nil)
}
func (s *callViewService) handleCallView(c echo.Context, functionHname isc.Hname) error {
chainID, err := isc.ChainIDFromString(c.Param("chainID"))
if err != nil {
return httperrors.BadRequest(fmt.Sprintf("Invalid chain ID: %+v", c.Param("chainID")))
}
contractHname, err := isc.HnameFromString(c.Param("contractHname"))
if err != nil {
return httperrors.BadRequest(fmt.Sprintf("Invalid contract ID: %+v", c.Param("contractHname")))
}
var params dict.Dict
if c.Request().Body != http.NoBody {
if err := json.NewDecoder(c.Request().Body).Decode(¶ms); err != nil {
return httperrors.BadRequest("Invalid request body")
}
}
theChain := s.chains().Get(chainID)
if theChain == nil {
return httperrors.NotFound(fmt.Sprintf("Chain not found: %s", chainID))
}
ret, err := chainutil.CallView(theChain, contractHname, functionHname, params)
if err != nil {
return httperrors.ServerError(fmt.Sprintf("View call failed: %v", err))
}
return c.JSON(http.StatusOK, ret)
}
func (s *callViewService) handleCallViewByName(c echo.Context) error {
fname := c.Param("fname")
return s.handleCallView(c, isc.Hn(fname))
}
func (s *callViewService) handleCallViewByHname(c echo.Context) error {
functionHname, err := isc.HnameFromString(c.Param("functionHname"))
if err != nil {
return httperrors.BadRequest(fmt.Sprintf("Invalid function ID: %+v", c.Param("functionHname")))
}
return s.handleCallView(c, functionHname)
}
func (s *callViewService) handleStateGet(c echo.Context) error {
chainID, err := isc.ChainIDFromString(c.Param("chainID"))
if err != nil {
return httperrors.BadRequest(fmt.Sprintf("Invalid chain ID: %+v", c.Param("chainID")))
}
key, err := hex.DecodeString(c.Param("key"))
if err != nil {
return httperrors.BadRequest(fmt.Sprintf("cannot parse hex-encoded key: %+v", c.Param("key")))
}
theChain := s.chains().Get(chainID)
if theChain == nil {
return httperrors.NotFound(fmt.Sprintf("Chain not found: %s", chainID))
}
var ret []byte
err = optimism.RetryOnStateInvalidated(func() error {
var err error
ret, err = theChain.GetStateReader().KVStoreReader().Get(kv.Key(key))
return err
})
if err != nil {
reason := fmt.Sprintf("View call failed: %v", err)
if errors.Is(err, coreutil.ErrorStateInvalidated) {
return httperrors.Conflict(reason)
}
return httperrors.ServerError(reason)
}
return c.JSON(http.StatusOK, ret)
}