diff --git a/.golangci.bck.yml b/.golangci.bck.yml new file mode 100644 index 0000000..4d01df2 --- /dev/null +++ b/.golangci.bck.yml @@ -0,0 +1,31 @@ +linters: + enable: + - asciicheck + - bodyclose + - dogsled + - dupl + - errcheck + - exportloopref + - goconst + - gofmt + - goimports + - revive + - gosimple + - govet + - ineffassign + - misspell + - nakedret + - nolintlint + - prealloc + - staticcheck + - stylecheck + - typecheck + - unconvert + - unused + +issues: + max-same-issues: 50 + +linters-settings: + dogsled: + max-blank-identifiers: 3 diff --git a/.golangci.yml b/.golangci.yml index 876c463..571997c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,35 +1,41 @@ +version: "2" linters: enable: - asciicheck - bodyclose - dogsled - dupl - - errcheck - - exportloopref - goconst - - gofmt - - goimports - - revive - - gosimple - - govet - - ineffassign - misspell - nakedret - nolintlint - prealloc + - revive - staticcheck - - stylecheck - - typecheck - unconvert - - unused - + settings: + dogsled: + max-blank-identifiers: 3 + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ issues: max-same-issues: 50 - -linters-settings: - dogsled: - max-blank-identifiers: 3 - golint: - min-confidence: 0 - maligned: - suggest-new: true +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/config.yaml b/config.yaml index 58b6a77..b233dbc 100644 --- a/config.yaml +++ b/config.yaml @@ -2,6 +2,7 @@ network: rpc: "" registration: "" lightweight_state: "" + likeness_registry: "" private_key: "" vault_address: "https://127.0.0.1:8200" vault_mount_path: "secret_data" diff --git a/docs/spec/components/schemas/LikenessRequest.yaml b/docs/spec/components/schemas/LikenessRequest.yaml new file mode 100644 index 0000000..424ba0f --- /dev/null +++ b/docs/spec/components/schemas/LikenessRequest.yaml @@ -0,0 +1,7 @@ +type: object +x-go-is-request: true +required: + - data +properties: + data: + $ref: '#/components/schemas/LikenessRequestData' diff --git a/docs/spec/components/schemas/LikenessRequestData.yaml b/docs/spec/components/schemas/LikenessRequestData.yaml new file mode 100644 index 0000000..c5e85fb --- /dev/null +++ b/docs/spec/components/schemas/LikenessRequestData.yaml @@ -0,0 +1,16 @@ +type: object +required: + - tx_data + - no_send +properties: + meta: + type: object + description: "Metadata if it is required" + example: {"source": "web", "user_id": 123} + tx_data: + type: string + no_send: + type: bool + description: Flag indicates whether transaction should be sent on-chain + example: true + diff --git a/docs/spec/paths/integrations@registration-relayer@v1@likeness-registry.yaml b/docs/spec/paths/integrations@registration-relayer@v1@likeness-registry.yaml new file mode 100644 index 0000000..c21f8db --- /dev/null +++ b/docs/spec/paths/integrations@registration-relayer@v1@likeness-registry.yaml @@ -0,0 +1,33 @@ +post: + tags: + - Likeness Registry + summary: Likeness Registry + operationId: likeness + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/LikenessRequest' + responses: + '200': + description: Success + content: + application/json: + schema: + type: object + properties: + data: + type: object + $ref: '#/components/schemas/Tx' + '400': + description: Bad Request Error + content: + application/json: + schema: + $ref: '#/components/schemas/Errors' + '500': + description: Internal Error + content: + application/json: + schema: + $ref: '#/components/schemas/Errors' \ No newline at end of file diff --git a/internal/config/network.go b/internal/config/network.go index e4dca31..309ef21 100644 --- a/internal/config/network.go +++ b/internal/config/network.go @@ -39,6 +39,7 @@ type RelayerConfig struct { RPC *ethclient.Client RegistrationAddress common.Address LightweightStateAddress *common.Address + LikenessRegistryAddress *common.Address ChainID *big.Int PrivateKey *ecdsa.PrivateKey WhiteList whitelist @@ -56,6 +57,7 @@ func (e *ethereum) RelayerConfig() *RelayerConfig { RPC *ethclient.Client `fig:"rpc,required"` RegistrationAddress common.Address `fig:"registration,required"` LightweightStateAddress *common.Address `fig:"lightweight_state"` + LikenessRegistryAddress *common.Address `fig:"likeness_registry"` PrivateKey *ecdsa.PrivateKey `fig:"private_key"` VaultAddress string `fig:"vault_address"` VaultMountPath string `fig:"vault_mount_path"` @@ -76,6 +78,7 @@ func (e *ethereum) RelayerConfig() *RelayerConfig { result.RPC = networkConfig.RPC result.RegistrationAddress = networkConfig.RegistrationAddress result.LightweightStateAddress = networkConfig.LightweightStateAddress + result.LikenessRegistryAddress = networkConfig.LikenessRegistryAddress result.ChainID, err = result.RPC.ChainID(context.Background()) if err != nil { diff --git a/internal/service/handlers/likeness_registry.go b/internal/service/handlers/likeness_registry.go new file mode 100644 index 0000000..d25db28 --- /dev/null +++ b/internal/service/handlers/likeness_registry.go @@ -0,0 +1,77 @@ +package handlers + +import ( + "net/http" + "strings" + + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/vm" + validation "github.com/go-ozzo/ozzo-validation/v4" + "github.com/rarimo/registration-relayer/internal/service/requests" + "gitlab.com/distributed_lab/ape" + "gitlab.com/distributed_lab/ape/problems" + "gitlab.com/distributed_lab/logan/v3" + "gitlab.com/distributed_lab/logan/v3/errors" +) + +func LikenessRegistry(w http.ResponseWriter, r *http.Request) { + req, err := requests.NewLikenessRequest(r) + if err != nil { + Log(r).WithError(err).Error("failed to get request") + ape.RenderErr(w, problems.BadRequest(err)...) + return + } + + logF := logan.F{ + "user-agent": r.Header.Get("User-Agent"), + "calldata": req.Data.TxData, + } + if req.Data.Meta != nil { + logF = logF.Merge(*req.Data.Meta) + } + + log := Log(r).WithFields(logF) + log.Debug("likeness registry request") + + if RelayerConfig(r).LikenessRegistryAddress == nil { + Log(r).Error("likeness registry address is not set in config") + ape.RenderErr(w, problems.InternalError()) + return + } + + var txd txData + txd.dataBytes, err = hexutil.Decode(req.Data.TxData) + if err != nil { + Log(r).WithError(err).Error("failed to decode data") + ape.RenderErr(w, problems.BadRequest(err)...) + return + } + + RelayerConfig(r).LockNonce() + defer RelayerConfig(r).UnlockNonce() + + err = confGas(r, &txd, RelayerConfig(r).LikenessRegistryAddress) + if err != nil { + Log(r).WithError(err).Error("failed to configure gas and gasPrice") + // `errors.Is` is not working for rpc errors, they passed as a string without additional wrapping + // because of this we operate with raw strings + if strings.Contains(err.Error(), vm.ErrExecutionReverted.Error()) { + errParts := strings.Split(err.Error(), ":") + contractName := strings.TrimSpace(errParts[len(errParts)-2]) + errMsg := errors.New(strings.TrimSpace(errParts[len(errParts)-1])) + ape.RenderErr(w, problems.BadRequest(validation.Errors{contractName: errMsg}.Filter())...) + return + } + ape.RenderErr(w, problems.InternalError()) + return + } + + tx, err := sendTx(r, &txd, RelayerConfig(r).LikenessRegistryAddress, req.Data.NoSend) + if err != nil { + Log(r).WithError(err).Error("failed to send tx") + ape.RenderErr(w, problems.InternalError()) + return + } + + ape.Render(w, newTxResponse(tx)) +} diff --git a/internal/service/requests/likeness_registry.go b/internal/service/requests/likeness_registry.go new file mode 100644 index 0000000..a6f726e --- /dev/null +++ b/internal/service/requests/likeness_registry.go @@ -0,0 +1,21 @@ +package requests + +import ( + "encoding/json" + "net/http" + + validation "github.com/go-ozzo/ozzo-validation/v4" + "github.com/rarimo/registration-relayer/resources" + "gitlab.com/distributed_lab/logan/v3/errors" +) + +func NewLikenessRequest(r *http.Request) (req resources.LikenessRequest, err error) { + err = json.NewDecoder(r.Body).Decode(&req) + if err != nil { + return req, errors.Wrap(err, "failed to unmarshal") + } + + return req, validation.Errors{ + "data/tx_data": validation.Validate(req.Data.TxData, validation.Required, validation.Match(calldataRegexp)), + }.Filter() +} diff --git a/internal/service/router.go b/internal/service/router.go index 9eb5584..02af292 100644 --- a/internal/service/router.go +++ b/internal/service/router.go @@ -21,6 +21,7 @@ func (s *service) router() chi.Router { r.Route("/v1", func(r chi.Router) { r.Post("/register", handlers.Registration) r.Post("/transit-state", handlers.TransitState) + r.Post("/likeness-registry", handlers.LikenessRegistry) }) }) diff --git a/resources/model_likeness_request.go b/resources/model_likeness_request.go new file mode 100644 index 0000000..a559ce9 --- /dev/null +++ b/resources/model_likeness_request.go @@ -0,0 +1,9 @@ +/* + * GENERATED. Do not modify. Your changes might be overwritten! + */ + +package resources + +type LikenessRequest struct { + Data LikenessRequestData `json:"data"` +} diff --git a/resources/model_likeness_request_data.go b/resources/model_likeness_request_data.go new file mode 100644 index 0000000..190dffe --- /dev/null +++ b/resources/model_likeness_request_data.go @@ -0,0 +1,13 @@ +/* + * GENERATED. Do not modify. Your changes might be overwritten! + */ + +package resources + +type LikenessRequestData struct { + // Metadata if it is required + Meta *map[string]interface{} `json:"meta,omitempty"` + // Flag indicates whether transaction should be sent on-chain + NoSend bool `json:"no_send"` + TxData string `json:"tx_data"` +}