diff --git a/README.md b/README.md index 0c2eccbd..415779c4 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ Explore detailed documentation for various components of Signatory: - [JWT Authentication](./docs/jwt_auth.md) - [Remote Policy Configuration](./docs/remote_policy.md) - [Signatory Architecture](./docs/signatory-architecture.md) +- [Etherlink Signer API](./docs/etherlink.md) ## Features diff --git a/cmd/commands/list_ops.go b/cmd/commands/list_ops.go index dab9a76c..81eaef1c 100644 --- a/cmd/commands/list_ops.go +++ b/cmd/commands/list_ops.go @@ -2,11 +2,11 @@ package commands import ( "os" - "sort" + "slices" "text/template" - "github.com/ecadlabs/gotez/v2/encoding" proto "github.com/ecadlabs/gotez/v2/protocol/latest" + "github.com/ecadlabs/gotez/v2/protocol/smartrollups/etherlink" "github.com/spf13/cobra" ) @@ -32,11 +32,12 @@ func NewListRequests(c *Context) *cobra.Command { Use: "list-requests", Short: "Print possible request types", RunE: func(cmd *cobra.Command, args []string) error { - var kinds []string - for _, k := range encoding.ListVariants[proto.SignRequest]() { - kinds = append(kinds, k.SignRequestKind()) - } - sort.Strings(kinds) + kinds := proto.ListSignRequests() + kinds = append(kinds, + (ðerlink.UnsignedSequencerBlueprint{}).SignRequestKind(), + etherlink.UnsignedDALSlotSignals{}.SignRequestKind(), + ) + slices.Sort(kinds) return listReqTpl.Execute(os.Stdout, kinds) }, } @@ -49,14 +50,9 @@ func NewListOps(c *Context) *cobra.Command { Use: "list-ops", Short: "Print possible operation types inside the `generic` request", RunE: func(cmd *cobra.Command, args []string) error { - var ops []string - for _, k := range proto.ListOperations() { - ops = append(ops, k.OperationKind()) - } - for _, op := range proto.ListPseudoOperations() { - ops = append(ops, op.PseudoOperation()) - } - sort.Strings(ops) + ops := proto.ListOperations() + ops = append(ops, proto.ListPseudoOperations()...) + slices.Sort(ops) return listOpsTpl.Execute(os.Stdout, ops) }, } diff --git a/docs/etherlink.md b/docs/etherlink.md new file mode 100644 index 00000000..d9e1c165 --- /dev/null +++ b/docs/etherlink.md @@ -0,0 +1,44 @@ +--- +id: etherlink +title: Etherlink Signer API +--- + +## API Calls + +### POST /keys/{key}/sequencer_blueprint + +Sign the RLP sequencer blueprint + +* Input + + Single JSON hexadecimal string containing unsigned sequencer blueprint + +* Output + + ```json + { + "signature": "...", // Base58 encoded signature + "signed_sequencer_blueprint": "..." // Hexadecimal string containing RLP encoded signed blueprint + } + ``` + +### POST /keys/{key}/sequencer_signal + +Sign the RLP sequencer signal aka DAL slot import signals + +* Input + + Single JSON hexadecimal string containing unsigned DAL slot import signals list + +* Output + + ```json + { + "signature": "...", // Base58 encoded signature + "signed_sequencer_signal": "..." // Hexadecimal string containing RLP encoded signed DAL slot import signals list + } + ``` + +## Key Policy + +There are two special request types `sequencer_blueprint` and `sequencer_signal` to be used to enable those calls for specific keys diff --git a/go.mod b/go.mod index ffd2bf77..293c6402 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/certusone/yubihsm-go v0.3.0 github.com/ecadlabs/go-pkcs11 v0.3.0 github.com/ecadlabs/goblst v1.1.0 - github.com/ecadlabs/gotez/v2 v2.3.8 + github.com/ecadlabs/gotez/v2 v2.3.11 github.com/fxamacker/cbor/v2 v2.8.0 github.com/go-playground/validator/v10 v10.26.0 github.com/google/tink/go v1.7.0 @@ -30,7 +30,7 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.10.0 - golang.org/x/crypto v0.39.0 + golang.org/x/crypto v0.40.0 golang.org/x/exp v0.0.0-20231127185646-65229373498e golang.org/x/oauth2 v0.29.0 google.golang.org/api v0.229.0 @@ -89,7 +89,7 @@ require ( go.opentelemetry.io/otel v1.35.0 // indirect go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.35.0 // indirect - golang.org/x/sync v0.15.0 // indirect + golang.org/x/sync v0.16.0 // indirect golang.org/x/time v0.11.0 // indirect google.golang.org/genproto v0.0.0-20250414145226-207652e42e2e // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e // indirect @@ -112,10 +112,10 @@ require ( github.com/prometheus/common v0.63.0 // indirect github.com/prometheus/procfs v0.16.0 // indirect github.com/spf13/pflag v1.0.6 // indirect - golang.org/x/net v0.39.0 // indirect - golang.org/x/sys v0.33.0 - golang.org/x/term v0.32.0 - golang.org/x/text v0.26.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/sys v0.34.0 + golang.org/x/term v0.33.0 + golang.org/x/text v0.27.0 // indirect google.golang.org/grpc v1.71.1 // indirect google.golang.org/protobuf v1.36.6 // indirect ) diff --git a/go.sum b/go.sum index 63a4a624..229c111d 100644 --- a/go.sum +++ b/go.sum @@ -67,8 +67,8 @@ github.com/ecadlabs/go-pkcs11 v0.3.0 h1:AsLURdNoZn0YocumJFloWXIlx1f2SDw4eTx4nPMa github.com/ecadlabs/go-pkcs11 v0.3.0/go.mod h1:PwAVBY0muwp6quQFmSDcB5Ekl4TjGG7cEQQwY9KpNVc= github.com/ecadlabs/goblst v1.1.0 h1:U0JzRxIbtv1OsefUqfXNJ1HLzPMSaHY8aOeIQggUe9U= github.com/ecadlabs/goblst v1.1.0/go.mod h1:s67gqaOol9o6fguh+evH75X5uQniOhv1HG/EU8xPLPY= -github.com/ecadlabs/gotez/v2 v2.3.8 h1:pLV1CaWTV9m9E4FzPUVH2hp6aIaLwJWfbMkOc77OG80= -github.com/ecadlabs/gotez/v2 v2.3.8/go.mod h1:GNR3XjOXxeeJceS0dTGvO2uNzpz2wgKRL9q6f5rMGiY= +github.com/ecadlabs/gotez/v2 v2.3.11 h1:ucyDxbsnWXXe3Pp2ehSS9M15zLBQeSxZU0FzmuYb4KY= +github.com/ecadlabs/gotez/v2 v2.3.11/go.mod h1:tgp2C5U1fgdniQwl//Jp5ejSB2suySXixD+a4GbK2s8= github.com/ecadlabs/pretty v0.0.0-20230412124801-f948fc689a04 h1:7WdblGykGxtGGtchW4kzTaJJO8Fm+JKhLzhttOOWr9k= github.com/ecadlabs/pretty v0.0.0-20230412124801-f948fc689a04/go.mod h1:VApUlocsLMpp4hUXHxTTIlosebnwo0BM6e1hy78qTPM= github.com/enceve/crypto v0.0.0-20160707101852-34d48bb93815 h1:D22EM5TeYZJp43hGDx6dUng8mvtyYbB9BnE3+BmJR1Q= @@ -215,27 +215,27 @@ go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstF go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= -golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= -golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= -golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= +golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg= +golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= -golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/pkg/server/server.go b/pkg/server/server.go index 56ab9341..a474355d 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -24,6 +24,8 @@ const defaultAddr = ":6732" // Signer interface representing a Signer (currently implemented by Signatory) type Signer interface { Sign(ctx context.Context, req *signatory.SignRequest) (crypt.Signature, error) + SignSequencerBlueprint(ctx context.Context, req *signatory.SignRequest) (crypt.Signature, []byte, error) + SignSequencerSignal(ctx context.Context, req *signatory.SignRequest) (crypt.Signature, []byte, error) ProvePossession(ctx context.Context, req *signatory.SignRequest) (crypt.Signature, error) GetPublicKey(ctx context.Context, keyHash crypt.PublicKeyHash) (*signatory.PublicKey, error) } @@ -79,11 +81,10 @@ func (s *Server) authenticateSignRequest(req *signatory.SignRequest, r *http.Req return errors.Wrap(stderr.New("invalid authentication signature"), http.StatusForbidden) } -func (s *Server) signHandler(w http.ResponseWriter, r *http.Request) { +func (s *Server) processInput(r *http.Request) (*signatory.SignRequest, error) { pkh, err := b58.ParsePublicKeyHash([]byte(mux.Vars(r)["key"])) if err != nil { - tezosJSONError(w, errors.Wrap(err, http.StatusBadRequest)) - return + return nil, errors.Wrap(err, http.StatusBadRequest) } signRequest := signatory.SignRequest{ PublicKeyHash: pkh, @@ -98,31 +99,37 @@ func (s *Server) signHandler(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { s.logger().Errorf("Error reading POST content: %v", err) - tezosJSONError(w, err) - return + return nil, err } var req string if err := json.Unmarshal(body, &req); err != nil { - tezosJSONError(w, errors.Wrap(err, http.StatusBadRequest)) - return + return nil, errors.Wrap(err, http.StatusBadRequest) } signRequest.Message, err = hex.DecodeString(req) if err != nil { - tezosJSONError(w, errors.Wrap(err, http.StatusBadRequest)) - return + return nil, errors.Wrap(err, http.StatusBadRequest) } if s.Auth != nil { if err = s.authenticateSignRequest(&signRequest, r); err != nil { s.logger().Error(err) - tezosJSONError(w, err) - return + return nil, err } } - signature, err := s.Signer.Sign(r.Context(), &signRequest) + return &signRequest, nil +} + +func (s *Server) signHandler(w http.ResponseWriter, r *http.Request) { + signRequest, err := s.processInput(r) + if err != nil { + tezosJSONError(w, err) + return + } + + signature, err := s.Signer.Sign(r.Context(), signRequest) if err != nil { s.logger().Errorf("Error signing request: %v", err) tezosJSONError(w, err) @@ -137,6 +144,54 @@ func (s *Server) signHandler(w http.ResponseWriter, r *http.Request) { jsonResponse(w, http.StatusOK, &resp) } +func (s *Server) sequencerBlueprintHandler(w http.ResponseWriter, r *http.Request) { + signRequest, err := s.processInput(r) + if err != nil { + tezosJSONError(w, err) + return + } + + signature, data, err := s.Signer.SignSequencerBlueprint(r.Context(), signRequest) + if err != nil { + s.logger().Errorf("Error signing request: %v", err) + tezosJSONError(w, err) + return + } + + resp := struct { + Signature crypt.Signature `json:"signature"` + SignedSequencerBlueprint string `json:"signed_sequencer_blueprint"` + }{ + Signature: signature, + SignedSequencerBlueprint: hex.EncodeToString(data), + } + jsonResponse(w, http.StatusOK, &resp) +} + +func (s *Server) sequencerSignalHandler(w http.ResponseWriter, r *http.Request) { + signRequest, err := s.processInput(r) + if err != nil { + tezosJSONError(w, err) + return + } + + signature, data, err := s.Signer.SignSequencerSignal(r.Context(), signRequest) + if err != nil { + s.logger().Errorf("Error signing request: %v", err) + tezosJSONError(w, err) + return + } + + resp := struct { + Signature crypt.Signature `json:"signature"` + SignedSequencerSignal string `json:"signed_sequencer_signal"` + }{ + Signature: signature, + SignedSequencerSignal: hex.EncodeToString(data), + } + jsonResponse(w, http.StatusOK, &resp) +} + func (s *Server) getKeyHandler(w http.ResponseWriter, r *http.Request) { keyHash := mux.Vars(r)["key"] pkh, err := b58.ParsePublicKeyHash([]byte(keyHash)) @@ -231,6 +286,8 @@ func (s *Server) Handler() (http.Handler, error) { r.Methods("POST").Path("/login").HandlerFunc(s.MidWare.LoginHandler) r.Methods("POST").Path("/keys/{key}").HandlerFunc(s.signHandler) + r.Methods("POST").Path("/keys/{key}/sequencer_blueprint").HandlerFunc(s.sequencerBlueprintHandler) + r.Methods("POST").Path("/keys/{key}/sequencer_signal").HandlerFunc(s.sequencerSignalHandler) r.Methods("GET").Path("/keys/{key}").HandlerFunc(s.getKeyHandler) r.Methods("GET").Path("/bls_prove_possession/{key}").HandlerFunc(s.blsProveHandler) r.Methods("GET").Path("/authorized_keys").HandlerFunc(s.authorizedKeysHandler) diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go index 62f0f126..14dbe17a 100644 --- a/pkg/server/server_test.go +++ b/pkg/server/server_test.go @@ -19,6 +19,7 @@ import ( type signerMock struct { SignResponse crypt.Signature + SignedData []byte SignError error PublicKeyResponse *signatory.PublicKey PublicKeyError error @@ -32,6 +33,15 @@ func (c *signerMock) ProvePossession(ctx context.Context, req *signatory.SignReq return c.SignResponse, c.SignError } +func (c *signerMock) SignSequencerBlueprint(ctx context.Context, req *signatory.SignRequest) (crypt.Signature, []byte, error) { + return c.SignResponse, c.SignedData, c.SignError + +} + +func (c *signerMock) SignSequencerSignal(ctx context.Context, req *signatory.SignRequest) (crypt.Signature, []byte, error) { + return c.SignResponse, c.SignedData, c.SignError +} + func (c *signerMock) GetPublicKey(ctx context.Context, keyHash crypt.PublicKeyHash) (*signatory.PublicKey, error) { if c.PublicKeyResponse == nil && c.PublicKeyError == nil { return nil, errors.New("key not found") diff --git a/pkg/signatory/signatory.go b/pkg/signatory/signatory.go index abb7374e..f912938d 100644 --- a/pkg/signatory/signatory.go +++ b/pkg/signatory/signatory.go @@ -14,11 +14,14 @@ import ( "strings" "sync" + tz "github.com/ecadlabs/gotez/v2" "github.com/ecadlabs/gotez/v2/b58" "github.com/ecadlabs/gotez/v2/crypt" "github.com/ecadlabs/gotez/v2/encoding" "github.com/ecadlabs/gotez/v2/protocol/core" proto "github.com/ecadlabs/gotez/v2/protocol/latest" + "github.com/ecadlabs/gotez/v2/protocol/smartrollups/etherlink" + "github.com/ecadlabs/gotez/v2/rlp" "github.com/ecadlabs/signatory/pkg/auth" "github.com/ecadlabs/signatory/pkg/config" "github.com/ecadlabs/signatory/pkg/errors" @@ -320,8 +323,7 @@ func (s *Signatory) callPolicyHook(ctx context.Context, req *SignRequest) error return nil } -// Sign ask the vault to sign a message with the private key associated to keyHash -func (s *Signatory) Sign(ctx context.Context, req *SignRequest) (crypt.Signature, error) { +func (s *Signatory) validateInput(ctx context.Context, req *SignRequest) (*PublicKeyPolicy, *log.Entry, error) { l := s.logger().WithField(logPKH, req.PublicKeyHash) if req.ClientPublicKeyHash != nil { @@ -332,24 +334,20 @@ func (s *Signatory) Sign(ctx context.Context, req *SignRequest) (crypt.Signature if policy == nil { err := fmt.Errorf("%s is not listed in config", strings.Replace(string(req.PublicKeyHash.ToBase58()), "\n", "", -1)) l.WithField(logRaw, hex.EncodeToString(req.Message)).Error(err) - return nil, errors.Wrap(err, http.StatusForbidden) + return nil, nil, errors.Wrap(err, http.StatusForbidden) } u := ctx.Value("user") if u != nil { if err := jwtVerifyUser(u.(string), policy, req); err != nil { l.WithField(logRaw, hex.EncodeToString(req.Message)).Error(err) - return nil, errors.Wrap(err, http.StatusForbidden) + return nil, nil, errors.Wrap(err, http.StatusForbidden) } } + return policy, l, nil +} - var msg proto.SignRequest - _, err := encoding.Decode(req.Message, &msg) - if err != nil { - l.WithField(logRaw, hex.EncodeToString(req.Message)).Error(strings.Replace(err.Error(), "\n", "", -1)) - return nil, errors.Wrap(err, http.StatusBadRequest) - } - +func (s *Signatory) signRequest(ctx context.Context, req *SignRequest, msg core.SignRequest, policy *PublicKeyPolicy, l *log.Entry) (crypt.Signature, error) { l = l.WithField(logReq, msg.SignRequestKind()) if m, ok := msg.(request.WithWatermark); ok { @@ -364,18 +362,15 @@ func (s *Signatory) Sign(ctx context.Context, req *SignRequest) (crypt.Signature p, err := s.getPublicKey(ctx, req.PublicKeyHash) if err != nil { - l.Error(err) return nil, err } l = l.WithField(logVault, p.key.Vault().Name()) if err = matchFilter(policy, req, msg); err != nil { - l.Error(err) return nil, errors.Wrap(err, http.StatusForbidden) } if err = s.callPolicyHook(ctx, req); err != nil { - l.Error(err) return nil, err } @@ -391,7 +386,6 @@ func (s *Signatory) Sign(ctx context.Context, req *SignRequest) (crypt.Signature signFunc := func(ctx context.Context, message []byte, key vault.KeyReference) (crypt.Signature, error) { if err = s.config.Watermark.IsSafeToSign(ctx, req.PublicKeyHash, msg, &digest); err != nil { err = errors.Wrap(err, http.StatusConflict) - l.Error(err) return nil, err } return key.Sign(ctx, message) @@ -422,6 +416,105 @@ func (s *Signatory) Sign(ctx context.Context, req *SignRequest) (crypt.Signature return sig, nil } +// Sign ask the vault to sign a message with the private key associated to keyHash +func (s *Signatory) Sign(ctx context.Context, req *SignRequest) (crypt.Signature, error) { + policy, l, err := s.validateInput(ctx, req) + if err != nil { + l.Error(err) + return nil, err + } + + var msg proto.SignRequest + _, err = encoding.Decode(req.Message, &msg) + if err != nil { + l.WithField(logRaw, hex.EncodeToString(req.Message)).Error(strings.Replace(err.Error(), "\n", "", -1)) + return nil, errors.Wrap(err, http.StatusBadRequest) + } + + sig, err := s.signRequest(ctx, req, msg, policy, l) + if err != nil { + l.Error(err) + } + return sig, err +} + +func signatureBytes(sig crypt.Signature) (tz.AnySignature, error) { + switch s := sig.ToProtocol().(type) { + case tz.ConventionalSignature: + return s.Generic()[:], nil + case *tz.BLSSignature: + return s[:], nil + default: + return nil, errors.New("invalid signature type") + } +} + +func (s *Signatory) SignSequencerBlueprint(ctx context.Context, req *SignRequest) (crypt.Signature, []byte, error) { + policy, l, err := s.validateInput(ctx, req) + if err != nil { + l.Error(err) + return nil, nil, err + } + + blueprint, err := rlp.Unmarshal[etherlink.UnsignedSequencerBlueprint](req.Message) + if err != nil { + l.Error(err) + return nil, nil, err + } + + sig, err := s.signRequest(ctx, req, blueprint, policy, l) + if err != nil { + l.Error(err) + return nil, nil, err + } + + sigBytes, err := signatureBytes(sig) + if err != nil { + l.Error(err) + return nil, nil, err + } + + signedBlueprint := etherlink.SequencerBlueprint{ + UnsignedSequencerBlueprint: *blueprint, + Signature: sigBytes, + } + signed, err := rlp.Marshal(&signedBlueprint) + return sig, signed, err +} + +func (s *Signatory) SignSequencerSignal(ctx context.Context, req *SignRequest) (crypt.Signature, []byte, error) { + policy, l, err := s.validateInput(ctx, req) + if err != nil { + l.Error(err) + return nil, nil, err + } + + signals, err := rlp.Unmarshal[etherlink.UnsignedDALSlotSignals](req.Message) + if err != nil { + l.Error(err) + return nil, nil, err + } + + sig, err := s.signRequest(ctx, req, *signals, policy, l) + if err != nil { + l.Error(err) + return nil, nil, err + } + + sigBytes, err := signatureBytes(sig) + if err != nil { + l.Error(err) + return nil, nil, err + } + + signedSignals := etherlink.DALSlotImportSignals{ + Signals: *signals, + Signature: sigBytes, + } + signed, err := rlp.Marshal(&signedSignals) + return sig, signed, err +} + func (s *Signatory) ProvePossession(ctx context.Context, req *SignRequest) (crypt.Signature, error) { l := s.logger().WithField(logPKH, req.PublicKeyHash)